home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / lib.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  147.2 KB  |  5,939 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. var FBL = XPCOMUtils;
  4.  
  5. try { /*@explore*/
  6.  
  7. (function() {
  8.  
  9. // ************************************************************************************************
  10. // Constants
  11.  
  12. const Cc = Components.classes;
  13. const Ci = Components.interfaces;
  14. this.fbs = Cc["@joehewitt.com/firebug;1"].getService().wrappedJSObject;
  15. this.jsd = this.CCSV("@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");
  16. const finder = this.finder = this.CCIN("@mozilla.org/embedcomp/rangefind;1", "nsIFind");
  17. const wm = this.CCSV("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
  18.  
  19. const PCMAP_SOURCETEXT = Ci.jsdIScript.PCMAP_SOURCETEXT;
  20. const PCMAP_PRETTYPRINT = Ci.jsdIScript.PCMAP_PRETTYPRINT;
  21.  
  22. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  23.  
  24. const reNotWhitespace = /[^\s]/;
  25. const reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
  26. const reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;  // This RE and the previous one should changed to be consistent
  27. // Globals
  28. this.reDataURL = /data:text\/javascript;fileName=([^;]*);baseLineNumber=(\d*?),((?:.*?%0A)|(?:.*))/g;
  29. this.reJavascript = /\s*javascript:\s*(.*)/;
  30. this.reChrome = /chrome:\/\/([^\/]*)\//;
  31. this.reCSS = /\.css$/;
  32. this.reFile = /file:\/\/([^\/]*)\//;
  33.  
  34. const reSplitLines = /\r\n|\r|\n/;
  35. const reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
  36. const reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
  37. const reWord = /([A-Za-z_$][A-Za-z_$0-9]*)(\.([A-Za-z_$][A-Za-z_$0-9]*))*/;
  38.  
  39. const restoreRetryTimeout = 500;
  40.  
  41. const NS_SEEK_SET = Ci.nsISeekableStream.NS_SEEK_SET;
  42.  
  43. // ************************************************************************************************
  44. // Namespaces
  45.  
  46. var namespaces = [];
  47.  
  48. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  49.  
  50. this.ns = function(fn)
  51. {
  52.     var ns = {};
  53.     namespaces.push(fn, ns);
  54.     return ns;
  55. };
  56.  
  57. this.initialize = function()
  58. {
  59.     for (var i = 0; i < namespaces.length; i += 2)
  60.     {
  61.         var fn = namespaces[i];
  62.         var ns = namespaces[i+1];
  63.         fn.apply(ns);
  64.     }
  65.                                                                                                                        /*@explore*/
  66. };
  67.  
  68. // ************************************************************************************************
  69. // Basics
  70.  
  71. this.bind = function()
  72. {
  73.    var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  74.    return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); }
  75. };
  76.  
  77. this.bindFixed = function()
  78. {
  79.     var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  80.     return function() { return fn.apply(object, args); }
  81. };
  82.  
  83. this.extend = function(l, r)
  84. {
  85.     var newOb = {};
  86.     for (var n in l)
  87.         newOb[n] = l[n];
  88.     for (var n in r)
  89.         newOb[n] = r[n];
  90.     return newOb;
  91. };
  92.  
  93. this.keys = function(map)  // At least sometimes the keys will be on user-level window objects
  94. {
  95.     var keys = [];
  96.     try
  97.     {
  98.         for (var name in map)  // enumeration is safe
  99.             keys.push(name);   // name is string, safe
  100.     }
  101.     catch (exc)
  102.     {
  103.         // Sometimes we get exceptions trying to iterate properties
  104.     }
  105.  
  106.     return keys;  // return is safe
  107. };
  108.  
  109. this.values = function(map)
  110. {
  111.     var values = [];
  112.     try
  113.     {
  114.         for (var name in map)
  115.         {
  116.             try
  117.             {
  118.                 values.push(map[name]);
  119.             }
  120.             catch (exc)
  121.             {
  122.                 // Sometimes we get exceptions trying to access properties
  123.             }
  124.  
  125.         }
  126.     }
  127.     catch (exc)
  128.     {
  129.         // Sometimes we get exceptions trying to iterate properties
  130.     }
  131.  
  132.     return values;
  133. };
  134.  
  135. this.remove = function(list, item)
  136. {
  137.     for (var i = 0; i < list.length; ++i)
  138.     {
  139.         if (list[i] == item)
  140.         {
  141.             list.splice(i, 1);
  142.             break;
  143.         }
  144.     }
  145. };
  146.  
  147. this.sliceArray = function(array, index)
  148. {
  149.     var slice = [];
  150.     for (var i = index; i < array.length; ++i)
  151.         slice.push(array[i]);
  152.  
  153.     return slice;
  154. };
  155.  
  156. function cloneArray(array, fn)
  157. {
  158.    var newArray = [];
  159.  
  160.    if (fn)
  161.        for (var i = 0; i < array.length; ++i)
  162.            newArray.push(fn(array[i]));
  163.    else
  164.        for (var i = 0; i < array.length; ++i)
  165.            newArray.push(array[i]);
  166.  
  167.    return newArray;
  168. }
  169.  
  170. function extendArray(array, array2)
  171. {
  172.    var newArray = [];
  173.    newArray.push.apply(newArray, array);
  174.    newArray.push.apply(newArray, array2);
  175.    return newArray;
  176. }
  177.  
  178. this.extendArray = extendArray;
  179. this.cloneArray = cloneArray;
  180.  
  181. function arrayInsert(array, index, other)
  182. {
  183.    for (var i = 0; i < other.length; ++i)
  184.        array.splice(i+index, 0, other[i]);
  185.  
  186.    return array;
  187. }
  188.  
  189. this.arrayInsert = arrayInsert;
  190.  
  191. this.safeToString = function(ob)
  192. {
  193.     try
  194.     {
  195.         return ob.toString();
  196.     }
  197.     catch (exc)
  198.     {
  199.         return "";
  200.     }
  201. };
  202.  
  203. this.convertToUnicode = function(text, charset)
  204. {
  205.     if (!text)
  206.         return "";
  207.  
  208.     try
  209.     {
  210.         var conv = this.CCSV("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
  211.         conv.charset = charset ? charset : "UTF-8";
  212.         var selectedCharset = conv.charset;
  213.         return conv.ConvertToUnicode(text);
  214.     }
  215.     catch (exc)
  216.     {
  217.         throw new Error("Firebug failed to convert to unicode using charset: "+conv.charset+" in @mozilla.org/intl/scriptableunicodeconverter");
  218.     }
  219. };
  220.  
  221. this.getPlatformName = function()
  222. {
  223.     return this.CCSV("@mozilla.org/xre/app-info;1", "nsIXULRuntime").OS;
  224. };
  225.  
  226. this.beep = function()
  227. {
  228.     var sounder = this.CCSV("@mozilla.org/sound;1", "nsISound");
  229.     sounder.beep();
  230. };
  231.  
  232. this.getUniqueId = function() {
  233.     return this.getRandomInt(0,65536);
  234. }
  235.  
  236. this.getRandomInt = function(min, max) {
  237.   return Math.floor(Math.random() * (max - min + 1) + min);
  238. }
  239.  
  240. this.createStyleSheet = function(doc, url)
  241. {
  242.     var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
  243.     style.setAttribute("charset","utf-8");
  244.     style.firebugIgnore = true;
  245.     style.setAttribute("type", "text/css");
  246.     style.innerHTML = this.getResource(url);
  247.     return style;
  248. }
  249.  
  250. this.addStyleSheet = function(doc, style)
  251. {
  252.     var heads = doc.getElementsByTagName("head");
  253.     if (heads.length)
  254.         heads[0].appendChild(style);
  255.     else
  256.         doc.documentElement.appendChild(style);
  257. };
  258.  
  259. this.addScript = function(doc, id, src)
  260. {
  261.     var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "script");
  262.     element.setAttribute("type", "text/javascript");
  263.     element.setAttribute("id", id);
  264.     element.firebugIgnore = true;
  265.  
  266.     element.innerHTML = src;
  267.     if (doc.documentElement)
  268.         doc.documentElement.appendChild(element);
  269.     else
  270.     {
  271.         // See issue 1079, the svg test case gives this error
  272.     }
  273. }
  274.  
  275. // ************************************************************************************************
  276. // Localization
  277.  
  278. /*
  279.  * $STR - intended for localization of a static string.
  280.  * $STRF - intended for localization of a string with dynamically inserted values.
  281.  * 
  282.  * Notes:
  283.  * 1) Name with _ in place of spaces is the key in the firebug.properties file.
  284.  * 2) If the specified key isn't localized for particular language, both methods use
  285.  *    the part after the last dot (in the specified name) as the return value.
  286.  *
  287.  * Examples:
  288.  * $STR("Label"); - search for key "Label" within the firebug.properties file 
  289.  *                 and returns its value. If the key doesn't exist returns "Label".
  290.  * 
  291.  * $STR("Button Label"); - search for key "Button_Label" withing the firebug.properties
  292.  *                        file. If the key doesn't exist returns "Button Label".
  293.  *
  294.  * $STR("net.Response Header"); - search for key "net.Response_Header". If the key doesn't 
  295.  *                               exist returns "Response Header".
  296.  *
  297.  * firebug.properties:
  298.  * net.timing.Request_Time=Request Time: %S [%S]
  299.  * 
  300.  * var param1 = 10;
  301.  * var param2 = "ms";
  302.  * $STRF("net.timing.Request Time", param1, param2);  -> "Request Time: 10 [ms]"
  303.  *                                          
  304.  * - search for key "net.timing.Request_Time" within the firebug.properties file. Parameters 
  305.  *   are inserted at specified places (%S) in the same order as they are passed. If the 
  306.  *   key doesn't exist the method returns "Request Time".
  307.  */
  308. function $STR(name) 
  309. {
  310.     try
  311.     {
  312.         return document.getElementById("strings_firebug").getString(name.replace(' ', '_', "g"));
  313.     }
  314.     catch (err)
  315.     {
  316.     }
  317.  
  318.     // Use only the label after last dot.
  319.     var index = name.lastIndexOf(".");
  320.     if (index > 0)
  321.         name = name.substr(index + 1);
  322.  
  323.     return name;
  324. }
  325.  
  326. function $STRF(name, args)
  327. {
  328.     try
  329.     {
  330.         return document.getElementById("strings_firebug").getFormattedString(name.replace(' ', '_', "g"), args);
  331.     }
  332.     catch (err)
  333.     {
  334.     }
  335.  
  336.     // Use only the label after last dot.
  337.     var index = name.lastIndexOf(".");
  338.     if (index > 0)
  339.         name = name.substr(index + 1);
  340.  
  341.     return name;
  342. }
  343.  
  344. this.$STR = $STR;
  345. this.$STRF = $STRF;
  346.  
  347. /*
  348.  * Use the current value of the attribute as a key to look up the localized value.
  349.  */
  350. this.internationalize = function(element, attr, args)
  351. {
  352.     if (typeof element == "string")
  353.         element = document.getElementById(element);
  354.  
  355.     var xulString = element.getAttribute(attr);
  356.     var localized = args ? $STRF(xulString, args) : $STR(xulString);
  357.  
  358.     // Set localized value of the attribute.
  359.     element.setAttribute(attr, localized);
  360. }
  361.  
  362. // ************************************************************************************************
  363. // Visibility
  364.  
  365. this.isVisible = function(elt)
  366. {
  367.     if (elt instanceof XULElement)
  368.     {
  369.         //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
  370.         return (!elt.hidden && !elt.collapsed);
  371.     }
  372.     return elt.offsetWidth > 0 || elt.offsetHeight > 0 || elt.localName in invisibleTags
  373.         || elt.namespaceURI == "http://www.w3.org/2000/svg";
  374. };
  375.  
  376. this.collapse = function(elt, collapsed)
  377. {
  378.     elt.setAttribute("collapsed", collapsed);
  379. };
  380.  
  381. this.obscure = function(elt, obscured)
  382. {
  383.     if (obscured)
  384.         this.setClass(elt, "obscured");
  385.     else
  386.         this.removeClass(elt, "obscured");
  387. };
  388.  
  389. this.hide = function(elt, hidden)
  390. {
  391.     elt.style.visibility = hidden ? "hidden" : "visible";
  392. };
  393.  
  394. this.clearNode = function(node)
  395. {
  396.     node.innerHTML = "";
  397. };
  398.  
  399. this.eraseNode = function(node)
  400. {
  401.     while (node.lastChild)
  402.         node.removeChild(node.lastChild);
  403. };
  404.  
  405. // ************************************************************************************************
  406. // Window iteration
  407.  
  408. this.iterateWindows = function(win, handler)
  409. {
  410.     if (!win || !win.document)
  411.         return;
  412.  
  413.     handler(win);
  414.  
  415.     if (win == top) return; // XXXjjb hack for chromeBug
  416.  
  417.     for (var i = 0; i < win.frames.length; ++i)
  418.     {
  419.         var subWin = win.frames[i];
  420.         if (subWin != win)
  421.             this.iterateWindows(subWin, handler);
  422.     }
  423. };
  424.  
  425. this.getRootWindow = function(win)
  426. {
  427.     for (; win; win = win.parent)
  428.     {
  429.         if (!win.parent || win == win.parent || !(win.parent instanceof Window) )
  430.             return win;
  431.     }
  432.     return null;
  433. };
  434.  
  435. // ************************************************************************************************
  436. // CSS classes
  437.  
  438. this.hasClass = function(node, name)
  439. {
  440.     if (!node || node.nodeType != 1)
  441.         return false;
  442.     else
  443.     {
  444.         for (var i=1; i<arguments.length; ++i)
  445.         {
  446.             var name = arguments[i];
  447.             var re = new RegExp("(^|\\s)"+name+"($|\\s)");
  448.             if (!re.exec(node.getAttribute("class")))
  449.                 return false;
  450.         }
  451.  
  452.         return true;
  453.     }
  454. };
  455.  
  456. this.setClass = function(node, name)
  457. {
  458.     if (node && !this.hasClass(node, name))
  459.         node.className += " " + name;
  460. };
  461.  
  462. this.getClassValue = function(node, name)
  463. {
  464.     var re = new RegExp(name+"-([^ ]+)");
  465.     var m = re.exec(node.className);
  466.     return m ? m[1] : "";
  467. };
  468.  
  469. this.removeClass = function(node, name)
  470. {
  471.     if (node && node.className)
  472.     {
  473.         var index = node.className.indexOf(name);
  474.         if (index >= 0)
  475.         {
  476.             var size = name.length;
  477.             node.className = node.className.substr(0,index-1) + node.className.substr(index+size);
  478.         }
  479.     }
  480. };
  481.  
  482. this.toggleClass = function(elt, name)
  483. {
  484.     if (this.hasClass(elt, name))
  485.         this.removeClass(elt, name);
  486.     else
  487.         this.setClass(elt, name);
  488. };
  489.  
  490. this.setClassTimed = function(elt, name, context, timeout)
  491. {
  492.     if (!timeout)
  493.         timeout = 1300;
  494.  
  495.     if (elt.__setClassTimeout)
  496.         context.clearTimeout(elt.__setClassTimeout);
  497.     else
  498.         this.setClass(elt, name);
  499.  
  500.     elt.__setClassTimeout = context.setTimeout(function()
  501.     {
  502.         delete elt.__setClassTimeout;
  503.  
  504.         FBL.removeClass(elt, name);
  505.     }, timeout);
  506. };
  507.  
  508. this.cancelClassTimed = function(elt, name, context)
  509. {
  510.     if (elt.__setClassTimeout)
  511.     {
  512.         FBL.removeClass(elt, name);
  513.         context.clearTimeout(elt.__setClassTimeout);
  514.         delete elt.__setClassTimeout;
  515.     }
  516. };
  517.  
  518. // ************************************************************************************************
  519. // DOM queries
  520.  
  521. this.$ = function(id, doc)
  522. {
  523.     if (doc)
  524.         return doc.getElementById(id);
  525.     else
  526.         return document.getElementById(id);
  527. };
  528.  
  529. this.getChildByClass = function(node) // ,classname, classname, classname...
  530. {
  531.     for (var i = 1; i < arguments.length; ++i)
  532.     {
  533.         var className = arguments[i];
  534.         var child = node.firstChild;
  535.         node = null;
  536.         for (; child; child = child.nextSibling)
  537.         {
  538.             if (this.hasClass(child, className))
  539.             {
  540.                 node = child;
  541.                 break;
  542.             }
  543.         }
  544.     }
  545.  
  546.     return node;
  547. };
  548.  
  549. this.getAncestorByClass = function(node, className)
  550. {
  551.     for (var parent = node; parent; parent = parent.parentNode)
  552.     {
  553.         if (this.hasClass(parent, className))
  554.             return parent;
  555.     }
  556.  
  557.     return null;
  558. };
  559.  
  560. this.getElementByClass = function(node, className)  // className, className, ...
  561. {
  562.     var args = cloneArray(arguments); args.splice(0, 1);
  563.     for (var child = node.firstChild; child; child = child.nextSibling)
  564.     {
  565.         var args1 = cloneArray(args); args1.unshift(child);
  566.         if (FBL.hasClass.apply(null, args1))
  567.             return child;
  568.         else
  569.         {
  570.             var found = FBL.getElementByClass.apply(null, args1);
  571.             if (found)
  572.                 return found;
  573.         }
  574.     }
  575.  
  576.     return null;
  577. };
  578.  
  579. this.getElementsByClass = function(node, className)  // className, className, ...
  580. {
  581.     function iteratorHelper(node, classNames, result)
  582.     {
  583.         for (var child = node.firstChild; child; child = child.nextSibling)
  584.         {
  585.             var args1 = cloneArray(classNames); args1.unshift(child);
  586.             if (FBL.hasClass.apply(null, args1))
  587.                 result.push(child);
  588.  
  589.             iteratorHelper(child, classNames, result);
  590.         }
  591.     }
  592.  
  593.     var result = [];
  594.     var args = cloneArray(arguments); args.shift();
  595.     iteratorHelper(node, args, result);
  596.     return result;
  597. };
  598.  
  599. this.isAncestor = function(node, potentialAncestor)
  600. {
  601.     for (var parent = node; parent; parent = parent.parentNode)
  602.     {
  603.         if (parent == potentialAncestor)
  604.             return true;
  605.     }
  606.  
  607.     return false;
  608. };
  609.  
  610. this.getNextElement = function(node)
  611. {
  612.     while (node && node.nodeType != 1)
  613.         node = node.nextSibling;
  614.  
  615.     return node;
  616. };
  617.  
  618. this.getPreviousElement = function(node)
  619. {
  620.     while (node && node.nodeType != 1)
  621.         node = node.previousSibling;
  622.  
  623.     return node;
  624. };
  625.  
  626. this.getBody = function(doc)
  627. {
  628.     if (doc.body)
  629.         return doc.body;
  630.  
  631.     var body = doc.getElementsByTagName("body")[0];
  632.     if (body)
  633.         return body;
  634.  
  635.     return doc.firstChild;  // For non-HTML docs
  636. };
  637.  
  638. this.findNextDown = function(node, criteria)
  639. {
  640.     if (!node)
  641.         return null;
  642.  
  643.     for (var child = node.firstChild; child; child = child.nextSibling)
  644.     {
  645.         if (criteria(child))
  646.             return child;
  647.  
  648.         var next = this.findNextDown(child, criteria);
  649.         if (next)
  650.             return next;
  651.     }
  652. };
  653.  
  654. this.findPreviousUp = function(node, criteria)
  655. {
  656.     if (!node)
  657.         return null;
  658.  
  659.     for (var child = node.lastChild; child; child = child.previousSibling)
  660.     {
  661.         var next = this.findPreviousUp(child, criteria);
  662.         if (next)
  663.             return next;
  664.  
  665.         if (criteria(child))
  666.             return child;
  667.     }
  668. };
  669.  
  670. this.findNext = function(node, criteria, upOnly, maxRoot)
  671. {
  672.     if (!node)
  673.         return null;
  674.  
  675.     if (!upOnly)
  676.     {
  677.         var next = this.findNextDown(node, criteria);
  678.         if (next)
  679.             return next;
  680.     }
  681.  
  682.     for (var sib = node.nextSibling; sib; sib = sib.nextSibling)
  683.     {
  684.         if (criteria(sib))
  685.             return sib;
  686.  
  687.         var next = this.findNextDown(sib, criteria);
  688.         if (next)
  689.             return next;
  690.     }
  691.  
  692.     if (node.parentNode && node.parentNode != maxRoot)
  693.         return this.findNext(node.parentNode, criteria, true);
  694. };
  695.  
  696. this.findPrevious = function(node, criteria, downOnly, maxRoot)
  697. {
  698.     if (!node)
  699.         return null;
  700.  
  701.     for (var sib = node.previousSibling; sib; sib = sib.previousSibling)
  702.     {
  703.         var prev = this.findPreviousUp(sib, criteria);
  704.         if (prev)
  705.             return prev;
  706.  
  707.         if (criteria(sib))
  708.             return sib;
  709.     }
  710.  
  711.     if (!downOnly)
  712.     {
  713.         var next = this.findPreviousUp(node, criteria);
  714.         if (next)
  715.             return next;
  716.     }
  717.  
  718.     if (node.parentNode && node.parentNode != maxRoot)
  719.     {
  720.         if (criteria(node.parentNode))
  721.             return node.parentNode;
  722.  
  723.         return this.findPrevious(node.parentNode, criteria, true);
  724.     }
  725. };
  726.  
  727. this.getNextByClass = function(root, state)
  728. {
  729.     function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
  730.     return this.findNext(root, iter);
  731. };
  732.  
  733. this.getPreviousByClass = function(root, state)
  734. {
  735.     function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
  736.     return this.findPrevious(root, iter);
  737. };
  738.  
  739. this.hasChildElements = function(node)
  740. {
  741.     if (node.contentDocument) // iframes
  742.         return true;
  743.  
  744.     for (var child = node.firstChild; child; child = child.nextSibling)
  745.     {
  746.         if (child.nodeType == 1)
  747.             return true;
  748.     }
  749.  
  750.     return false;
  751. };
  752.  
  753. this.isElement = function(o)
  754. {
  755.     try {
  756.         return o && o instanceof Element;
  757.     }
  758.     catch (ex) {
  759.         return false;
  760.     }
  761. };
  762.  
  763. this.isNode = function(o)
  764. {
  765.     try {
  766.         return o && o instanceof Node;
  767.     }
  768.     catch (ex) {
  769.         return false;
  770.     }
  771. };
  772.  
  773. // ************************************************************************************************
  774. // DOM Modification
  775.  
  776. this.setOuterHTML = function(element, html)
  777. {
  778.     var doc = element.ownerDocument;
  779.     var range = doc.createRange();
  780.     range.selectNode(element || doc.documentElement);
  781.  
  782.     var fragment = range.createContextualFragment(html);
  783.     var first = fragment.firstChild;
  784.     var last = fragment.lastChild;
  785.     element.parentNode.replaceChild(fragment, element);
  786.     return [first, last];
  787. };
  788.  
  789. this.appendInnerHTML = function(element, html)
  790. {
  791.     var doc = element.ownerDocument;
  792.     var range = doc.createRange();
  793.     range.selectNode(doc.body);
  794.  
  795.     var fragment = range.createContextualFragment(html);
  796.     element.appendChild(fragment);
  797. };
  798.  
  799. this.insertTextIntoElement = function(element, text)
  800. {
  801.     var command = "cmd_insertText";
  802.  
  803.     var controller = element.controllers.getControllerForCommand(command);
  804.     if (!controller || !controller.isCommandEnabled(command))
  805.         return;
  806.  
  807.     var params = this.CCIN("@mozilla.org/embedcomp/command-params;1", "nsICommandParams");
  808.     params.setStringValue("state_data", text);
  809.  
  810.     controller = this.QI(controller, Ci.nsICommandController);
  811.     controller.doCommandWithParams(command, params);
  812. };
  813.  
  814. // ************************************************************************************************
  815. // XPath
  816.  
  817. /**
  818.  * Gets an XPath for an element which describes its hierarchical location.
  819.  */
  820. this.getElementXPath = function(element)
  821. {
  822.     if (element && element.id)
  823.         return '//*[@id="' + element.id + '"]';
  824.     else
  825.         return this.getElementTreeXPath(element);
  826. };
  827.  
  828. this.getElementTreeXPath = function(element)
  829. {
  830.     var paths = [];
  831.  
  832.     for (; element && element.nodeType == 1; element = element.parentNode)
  833.     {
  834.         var index = 0;
  835.         for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling)
  836.         {
  837.             if (sibling.localName == element.localName)
  838.                 ++index;
  839.         }
  840.  
  841.         var tagName = element.localName.toLowerCase();
  842.         var pathIndex = (index ? "[" + (index+1) + "]" : "");
  843.         paths.splice(0, 0, tagName + pathIndex);
  844.     }
  845.  
  846.     return paths.length ? "/" + paths.join("/") : null;
  847. };
  848.  
  849. this.cssToXPath = function(rule)
  850. {
  851.     var regElement = /^([#.]?)([a-z0-9\\*_-]*)((\|)([a-z0-9\\*_-]*))?/i;
  852.     var regAttr1 = /^\[([^\]]*)\]/i;
  853.     var regAttr2 = /^\[\s*([^~=\s]+)\s*(~?=)\s*"([^"]+)"\s*\]/i;
  854.     var regPseudo = /^:([a-z_-])+/i;
  855.     var regCombinator = /^(\s*[>+\s])?/i;
  856.     var regComma = /^\s*,/i;
  857.  
  858.     var index = 1;
  859.     var parts = ["//", "*"];
  860.     var lastRule = null;
  861.  
  862.     while (rule.length && rule != lastRule)
  863.     {
  864.         lastRule = rule;
  865.  
  866.         // Trim leading whitespace
  867.         rule = this.trimLeft(rule);
  868.         if (!rule.length)
  869.             break;
  870.  
  871.         // Match the element identifier
  872.         var m = regElement.exec(rule);
  873.         if (m)
  874.         {
  875.             if (!m[1])
  876.             {
  877.                 // XXXjoe Namespace ignored for now
  878.                 if (m[5])
  879.                     parts[index] = m[5];
  880.                 else
  881.                     parts[index] = m[2];
  882.             }
  883.             else if (m[1] == '#')
  884.                 parts.push("[@id='" + m[2] + "']");
  885.             else if (m[1] == '.')
  886.                 parts.push("[contains(@class, '" + m[2] + "')]");
  887.  
  888.             rule = rule.substr(m[0].length);
  889.         }
  890.  
  891.         // Match attribute selectors
  892.         m = regAttr2.exec(rule);
  893.         if (m)
  894.         {
  895.             if (m[2] == "~=")
  896.                 parts.push("[contains(@" + m[1] + ", '" + m[3] + "')]");
  897.             else
  898.                 parts.push("[@" + m[1] + "='" + m[3] + "']");
  899.  
  900.             rule = rule.substr(m[0].length);
  901.         }
  902.         else
  903.         {
  904.             m = regAttr1.exec(rule);
  905.             if (m)
  906.             {
  907.                 parts.push("[@" + m[1] + "]");
  908.                 rule = rule.substr(m[0].length);
  909.             }
  910.         }
  911.  
  912.         // Skip over pseudo-classes and pseudo-elements, which are of no use to us
  913.         m = regPseudo.exec(rule);
  914.         while (m)
  915.         {
  916.             rule = rule.substr(m[0].length);
  917.             m = regPseudo.exec(rule);
  918.         }
  919.  
  920.         // Match combinators
  921.         m = regCombinator.exec(rule);
  922.         if (m && m[0].length)
  923.         {
  924.             if (m[0].indexOf(">") != -1)
  925.                 parts.push("/");
  926.             else if (m[0].indexOf("+") != -1)
  927.                 parts.push("/following-sibling::");
  928.             else
  929.                 parts.push("//");
  930.  
  931.             index = parts.length;
  932.             parts.push("*");
  933.             rule = rule.substr(m[0].length);
  934.         }
  935.  
  936.         m = regComma.exec(rule);
  937.         if (m)
  938.         {
  939.             parts.push(" | ", "//", "*");
  940.             index = parts.length-1;
  941.             rule = rule.substr(m[0].length);
  942.         }
  943.     }
  944.  
  945.     var xpath = parts.join("");
  946.     return xpath;
  947. };
  948.  
  949. this.getElementsBySelector = function(doc, css)
  950. {
  951.     var xpath = this.cssToXPath(css);
  952.     return this.getElementsByXPath(doc, xpath);
  953. };
  954.  
  955. this.getElementsByXPath = function(doc, xpath)
  956. {
  957.     var nodes = [];
  958.  
  959.     try {
  960.         var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
  961.         for (var item = result.iterateNext(); item; item = result.iterateNext())
  962.             nodes.push(item);
  963.     }
  964.     catch (exc)
  965.     {
  966.         // Invalid xpath expressions make their way here sometimes.  If that happens,
  967.         // we still want to return an empty set without an exception.
  968.     }
  969.  
  970.     return nodes;
  971. };
  972.  
  973. this.getRuleMatchingElements = function(rule, doc)
  974. {
  975.     var css = rule.selectorText;
  976.     var xpath = this.cssToXPath(css);
  977.     return this.getElementsByXPath(doc, xpath);
  978. };
  979.  
  980. // ************************************************************************************************
  981. // Clipboard
  982.  
  983. this.copyToClipboard = function(string)
  984. {
  985.     var clipboard = this.CCSV("@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
  986.     clipboard.copyString(string);
  987. };
  988.  
  989. // ************************************************************************************************
  990. // Graphics
  991.  
  992. this.getClientOffset = function(elt)
  993. {
  994.     function addOffset(elt, coords, view)
  995.     {
  996.         var p = elt.offsetParent;
  997.  
  998.         var style = view.getComputedStyle(elt, "");
  999.  
  1000.         if (elt.offsetLeft)
  1001.             coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth);
  1002.         if (elt.offsetTop)
  1003.             coords.y += elt.offsetTop + parseInt(style.borderTopWidth);
  1004.  
  1005.         if (p)
  1006.         {
  1007.             if (p.nodeType == 1)
  1008.                 addOffset(p, coords, view);
  1009.         }
  1010.         else if (elt.ownerDocument.defaultView.frameElement)
  1011.             addOffset(elt.ownerDocument.defaultView.frameElement, coords, elt.ownerDocument.defaultView);
  1012.     }
  1013.  
  1014.     var coords = {x: 0, y: 0};
  1015.     if (elt)
  1016.     {
  1017.         var view = elt.ownerDocument.defaultView;
  1018.         addOffset(elt, coords, view);
  1019.     }
  1020.  
  1021.     return coords;
  1022. };
  1023.  
  1024. this.getViewOffset = function(elt, singleFrame)
  1025. {
  1026.     function addOffset(elt, coords, view)
  1027.     {
  1028.         var p = elt.offsetParent;
  1029.         coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0);
  1030.         coords.y += elt.offsetTop - (p ? p.scrollTop : 0);
  1031.  
  1032.         if (p)
  1033.         {
  1034.             if (p.nodeType == 1)
  1035.             {
  1036.                 var parentStyle = view.getComputedStyle(p, "");
  1037.                 if (parentStyle.position != "static")
  1038.                 {
  1039.                     coords.x += parseInt(parentStyle.borderLeftWidth);
  1040.                     coords.y += parseInt(parentStyle.borderTopWidth);
  1041.  
  1042.                     if (p.localName == "TABLE")
  1043.                     {
  1044.                         coords.x += parseInt(parentStyle.paddingLeft);
  1045.                         coords.y += parseInt(parentStyle.paddingTop);
  1046.                     }
  1047.                     else if (p.localName == "BODY")
  1048.                     {
  1049.                         var style = view.getComputedStyle(elt, "");
  1050.                         coords.x += parseInt(style.marginLeft);
  1051.                         coords.y += parseInt(style.marginTop);
  1052.                     }
  1053.                 }
  1054.                 else if (p.localName == "BODY")
  1055.                 {
  1056.                     coords.x += parseInt(parentStyle.borderLeftWidth);
  1057.                     coords.y += parseInt(parentStyle.borderTopWidth);
  1058.                 }
  1059.  
  1060.                 var parent = elt.parentNode;
  1061.                 while (p != parent)
  1062.                 {
  1063.                     coords.x -= parent.scrollLeft;
  1064.                     coords.y -= parent.scrollTop;
  1065.                     parent = parent.parentNode;
  1066.                 }
  1067.                 addOffset(p, coords, view);
  1068.             }
  1069.         }
  1070.         else
  1071.         {
  1072.             if (elt.localName == "BODY")
  1073.             {
  1074.                 var style = view.getComputedStyle(elt, "");
  1075.                 coords.x += parseInt(style.borderLeftWidth);
  1076.                 coords.y += parseInt(style.borderTopWidth);
  1077.  
  1078.                 var htmlStyle = view.getComputedStyle(elt.parentNode, "");
  1079.                 coords.x -= parseInt(htmlStyle.paddingLeft);
  1080.                 coords.y -= parseInt(htmlStyle.paddingTop);
  1081.             }
  1082.  
  1083.             if (elt.scrollLeft)
  1084.                 coords.x += elt.scrollLeft;
  1085.             if (elt.scrollTop)
  1086.                 coords.y += elt.scrollTop;
  1087.  
  1088.             var win = elt.ownerDocument.defaultView;
  1089.             if (win && (!singleFrame && win.frameElement))
  1090.                 addOffset(win.frameElement, coords, win);
  1091.         }
  1092.  
  1093.     }
  1094.  
  1095.     var coords = {x: 0, y: 0};
  1096.     if (elt)
  1097.         addOffset(elt, coords, elt.ownerDocument.defaultView);
  1098.  
  1099.     return coords;
  1100. };
  1101.  
  1102. this.getOffsetSize = function(elt)
  1103. {
  1104.     return {width: elt.offsetWidth, height: elt.offsetHeight};
  1105. };
  1106.  
  1107. this.getOverflowParent = function(element)
  1108. {
  1109.     for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent)
  1110.     {
  1111.         if (scrollParent.scrollHeight > scrollParent.offsetHeight)
  1112.             return scrollParent;
  1113.     }
  1114. };
  1115.  
  1116. this.isScrolledToBottom = function(element)
  1117. {
  1118.     return element.scrollTop + element.offsetHeight == element.scrollHeight;
  1119. };
  1120.  
  1121. this.scrollToBottom = function(element)
  1122. {
  1123.     element.scrollTop = element.scrollHeight - element.offsetHeight;
  1124. };
  1125.  
  1126. this.move = function(element, x, y)
  1127. {
  1128.     element.style.left = x + "px";
  1129.     element.style.top = y + "px";
  1130. };
  1131.  
  1132. this.resize = function(element, w, h)
  1133. {
  1134.     element.style.width = w + "px";
  1135.     element.style.height = h + "px";
  1136. };
  1137.  
  1138. this.linesIntoCenterView = function(element, scrollBox)  // {before: int, after: int}
  1139. {
  1140.     if (!scrollBox)
  1141.         scrollBox = this.getOverflowParent(element);
  1142.  
  1143.     if (!scrollBox)
  1144.         return;
  1145.  
  1146.     var offset = this.getClientOffset(element);
  1147.  
  1148.     var topSpace = offset.y - scrollBox.scrollTop;
  1149.     var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
  1150.             - (offset.y + element.offsetHeight);
  1151.  
  1152.     if (topSpace < 0 || bottomSpace < 0)
  1153.     {
  1154.         var split = (scrollBox.clientHeight/2);
  1155.         var centerY = offset.y - split;
  1156.         scrollBox.scrollTop = centerY;
  1157.         topSpace = split;
  1158.         bottomSpace = split -  element.offsetHeight;
  1159.     }
  1160.  
  1161.     return {before: Math.round((topSpace/element.offsetHeight) + 0.5),
  1162.             after: Math.round((bottomSpace/element.offsetHeight) + 0.5) }
  1163. };
  1164.  
  1165. this.scrollIntoCenterView = function(element, scrollBox, notX, notY)
  1166. {
  1167.     if (!element)
  1168.         return;
  1169.  
  1170.     if (!scrollBox)
  1171.         scrollBox = this.getOverflowParent(element);
  1172.  
  1173.     if (!scrollBox)
  1174.         return;
  1175.  
  1176.     var offset = this.getClientOffset(element);
  1177.  
  1178.     if (!notY)
  1179.     {
  1180.         var topSpace = offset.y - scrollBox.scrollTop;
  1181.         var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
  1182.             - (offset.y + element.offsetHeight);
  1183.  
  1184.         if (topSpace < 0 || bottomSpace < 0)
  1185.         {
  1186.             var centerY = offset.y - (scrollBox.clientHeight/2);
  1187.             scrollBox.scrollTop = centerY;
  1188.         }
  1189.     }
  1190.  
  1191.     if (!notX)
  1192.     {
  1193.         var leftSpace = offset.x - scrollBox.scrollLeft;
  1194.         var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth)
  1195.             - (offset.x + element.clientWidth);
  1196.  
  1197.         if (leftSpace < 0 || rightSpace < 0)
  1198.         {
  1199.             var centerX = offset.x - (scrollBox.clientWidth/2);
  1200.             scrollBox.scrollLeft = centerX;
  1201.         }
  1202.     }
  1203. };
  1204.  
  1205. // ************************************************************************************************
  1206. // CSS
  1207.  
  1208. var cssKeywordMap = null;
  1209. var cssPropNames = null;
  1210. var cssColorNames = null;
  1211.  
  1212. this.getCSSKeywordsByProperty = function(propName)
  1213. {
  1214.     if (!cssKeywordMap)
  1215.     {
  1216.         cssKeywordMap = {};
  1217.  
  1218.         for (var name in this.cssInfo)
  1219.         {
  1220.             var list = [];
  1221.  
  1222.             var types = this.cssInfo[name];
  1223.             for (var i = 0; i < types.length; ++i)
  1224.             {
  1225.                 var keywords = this.cssKeywords[types[i]];
  1226.                 if (keywords)
  1227.                     list.push.apply(list, keywords);
  1228.             }
  1229.  
  1230.             cssKeywordMap[name] = list;
  1231.         }
  1232.     }
  1233.  
  1234.     return propName in cssKeywordMap ? cssKeywordMap[propName] : [];
  1235. };
  1236.  
  1237. this.getCSSPropertyNames = function()
  1238. {
  1239.     if (!cssPropNames)
  1240.     {
  1241.         cssPropNames = [];
  1242.  
  1243.         for (var name in this.cssInfo)
  1244.             cssPropNames.push(name);
  1245.     }
  1246.  
  1247.     return cssPropNames;
  1248. };
  1249.  
  1250. this.isColorKeyword = function(keyword)
  1251. {
  1252.     if (keyword == "transparent")
  1253.         return false;
  1254.  
  1255.     if (!cssColorNames)
  1256.     {
  1257.         cssColorNames = [];
  1258.  
  1259.         var colors = this.cssKeywords["color"];
  1260.         for (var i = 0; i < colors.length; ++i)
  1261.             cssColorNames.push(colors[i].toLowerCase());
  1262.  
  1263.         var systemColors = this.cssKeywords["systemColor"];
  1264.         for (var i = 0; i < systemColors.length; ++i)
  1265.             cssColorNames.push(systemColors[i].toLowerCase());
  1266.     }
  1267.  
  1268.     return cssColorNames.indexOf(keyword.toLowerCase()) != -1;
  1269. };
  1270.  
  1271. this.copyTextStyles = function(fromNode, toNode, style)
  1272. {
  1273.     var view = fromNode.ownerDocument.defaultView;
  1274.     if (view)
  1275.     {
  1276.         if (!style)
  1277.             style = view.getComputedStyle(fromNode, "");
  1278.  
  1279.         toNode.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
  1280.         toNode.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
  1281.         toNode.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
  1282.         toNode.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
  1283.  
  1284.         return style;
  1285.     }
  1286. };
  1287.  
  1288. this.copyBoxStyles = function(fromNode, toNode, style)
  1289. {
  1290.     var view = fromNode.ownerDocument.defaultView;
  1291.     if (view)
  1292.     {
  1293.         if (!style)
  1294.             style = view.getComputedStyle(fromNode, "");
  1295.  
  1296.         toNode.style.marginTop = style.getPropertyCSSValue("margin-top").cssText;
  1297.         toNode.style.marginRight = style.getPropertyCSSValue("margin-right").cssText;
  1298.         toNode.style.marginBottom = style.getPropertyCSSValue("margin-bottom").cssText;
  1299.         toNode.style.marginLeft = style.getPropertyCSSValue("margin-left").cssText;
  1300.         toNode.style.borderTopWidth = style.getPropertyCSSValue("border-top-width").cssText;
  1301.         toNode.style.borderRightWidth = style.getPropertyCSSValue("border-right-width").cssText;
  1302.         toNode.style.borderBottomWidth = style.getPropertyCSSValue("border-bottom-width").cssText;
  1303.         toNode.style.borderLeftWidth = style.getPropertyCSSValue("border-left-width").cssText;
  1304.  
  1305.         return style;
  1306.     }
  1307. };
  1308.  
  1309. this.readBoxStyles = function(style)
  1310. {
  1311.     const styleNames = {
  1312.         "margin-top": "marginTop", "margin-right": "marginRight",
  1313.         "margin-left": "marginLeft", "margin-bottom": "marginBottom",
  1314.         "border-top-width": "borderTop", "border-right-width": "borderRight",
  1315.         "border-left-width": "borderLeft", "border-bottom-width": "borderBottom",
  1316.         "padding-top": "paddingTop", "padding-right": "paddingRight",
  1317.         "padding-left": "paddingLeft", "padding-bottom": "paddingBottom"
  1318.     };
  1319.  
  1320.     var styles = {};
  1321.     for (var styleName in styleNames)
  1322.         styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText);
  1323.     return styles;
  1324. };
  1325.  
  1326. this.getBoxFromStyles = function(style, element)
  1327. {
  1328.     var args = this.readBoxStyles(style);
  1329.     args.width = element.offsetWidth
  1330.         - (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight);
  1331.     args.height = element.offsetHeight
  1332.         - (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom);
  1333.     return args;
  1334. };
  1335.  
  1336. this.getElementCSSSelector = function(element)
  1337. {
  1338.     var label = element.localName.toLowerCase();
  1339.     if (element.id)
  1340.         label += "#" + element.id;
  1341.     if (element.hasAttribute("class"))
  1342.         label += "." + element.getAttribute("class").split(" ")[0];
  1343.  
  1344.     return label;
  1345. };
  1346.  
  1347. this.getURLForStyleSheet= function(styleSheet)
  1348. {
  1349.     return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL);
  1350. };
  1351.  
  1352. // ************************************************************************************************
  1353. // XML Serialization
  1354.  
  1355. this.getElementXML = function(element)
  1356. {
  1357.     function toXML(elt)
  1358.     {
  1359.         if (elt.nodeType == 1)
  1360.         {
  1361.             xml.push('<', elt.localName.toLowerCase());
  1362.  
  1363.             for (var i = 0; i < elt.attributes.length; ++i)
  1364.             {
  1365.                 var attr = elt.attributes[i];
  1366.  
  1367.                 // Hide attributes set by Firebug
  1368.                 if (attr.localName.indexOf("firebug-") == 0)
  1369.                     continue;
  1370.  
  1371.                 xml.push(' ', attr.localName, '=', escapeHTMLAttribute(attr.nodeValue));
  1372.             }
  1373.  
  1374.             if (elt.firstChild)
  1375.             {
  1376.                 xml.push('>');
  1377.  
  1378.                 for (var child = elt.firstChild; child; child = child.nextSibling)
  1379.                     toXML(child);
  1380.  
  1381.                 xml.push('</', elt.localName.toLowerCase(), '>');
  1382.             }
  1383.             else
  1384.                 xml.push('/>');
  1385.         }
  1386.         else if (elt.nodeType == 3)
  1387.             xml.push(elt.nodeValue);
  1388.         else if (elt.nodeType == 4)
  1389.             xml.push('<![CDATA[', elt.nodeValue, ']]>');
  1390.         else if (elt.nodeType == 8)
  1391.             xml.push('<!--', elt.nodeValue, '-->');
  1392.     }
  1393.  
  1394.     var xml = [];
  1395.     toXML(element);
  1396.     return xml.join("");
  1397. };
  1398.  
  1399. // ************************************************************************************************
  1400. // String escaping
  1401.  
  1402. this.escapeNewLines = function(value)
  1403. {
  1404.     return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
  1405. };
  1406.  
  1407. this.stripNewLines = function(value)
  1408. {
  1409.     return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value;
  1410. };
  1411.  
  1412. this.escapeJS = function(value)
  1413. {
  1414.     return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g");
  1415. };
  1416.  
  1417. function escapeHTMLAttribute(value)
  1418. {
  1419.     function replaceChars(ch)
  1420.     {
  1421.         switch (ch)
  1422.         {
  1423.             case "&":
  1424.                 return "&";
  1425.             case "'":
  1426.                 return apos;
  1427.             case '"':
  1428.                 return quot;
  1429.         }
  1430.         return "?";
  1431.     };
  1432.     var apos = "'", quot = """, around = '"';
  1433.     if( value.indexOf('"') == -1 ) {
  1434.         quot = '"';
  1435.         apos = "'";
  1436.     } else if( value.indexOf("'") == -1 ) {
  1437.         quot = '"';
  1438.         around = "'";
  1439.     }
  1440.     return around + (String(value).replace(/[&'"]/g, replaceChars)) + around;
  1441. }
  1442.  
  1443.  
  1444. function escapeHTML(value)
  1445. {
  1446.     function replaceChars(ch)
  1447.     {
  1448.         switch (ch)
  1449.         {
  1450.             case "<":
  1451.                 return "<";
  1452.             case ">":
  1453.                 return ">";
  1454.             case "&":
  1455.                 return "&";
  1456.             case "'":
  1457.                 return "'";
  1458.             case '"':
  1459.                 return """;
  1460.         }
  1461.         return "?";
  1462.     };
  1463.     return String(value).replace(/[<>&"']/g, replaceChars);
  1464. }
  1465.  
  1466. this.escapeHTML = escapeHTML;
  1467.  
  1468. this.cropString = function(text, limit)
  1469. {
  1470.     text = text + "";
  1471.  
  1472.     if (!limit)
  1473.         var halfLimit = 50;
  1474.     else
  1475.         var halfLimit = limit / 2;
  1476.  
  1477.     if (text.length > limit)
  1478.         return this.escapeNewLines(text.substr(0, halfLimit) + "..." + text.substr(text.length-halfLimit));
  1479.     else
  1480.         return this.escapeNewLines(text);
  1481. };
  1482.  
  1483. this.isWhitespace = function(text)
  1484. {
  1485.     return !reNotWhitespace.exec(text);
  1486. };
  1487.  
  1488. this.splitLines = function(text)
  1489. {
  1490.     if (text.split)
  1491.         return text.split(reSplitLines);
  1492.     else
  1493.     {
  1494.         var str = text+"";
  1495.         var theSplit = str.split(reSplitLines);
  1496.         return theSplit;
  1497.     }
  1498. };
  1499.  
  1500. this.trimLeft = function(text)
  1501. {
  1502.     return text.replace(/^\s*|\s*$/g,"");
  1503. }
  1504.  
  1505. this.wrapText = function(text, noEscapeHTML)
  1506. {
  1507.     var reNonAlphaNumeric = /[^A-Za-z_$0-9'"-]/;
  1508.  
  1509.     var html = [];
  1510.     var wrapWidth = Firebug.textWrapWidth;
  1511.  
  1512.     var lines = this.splitLines(text);
  1513.     for (var i = 0; i < lines.length; ++i)
  1514.     {
  1515.         var line = lines[i];
  1516.         while (line.length > wrapWidth)
  1517.         {
  1518.             var m = reNonAlphaNumeric.exec(line.substr(wrapWidth, 100));
  1519.             var wrapIndex = wrapWidth+ (m ? m.index : 0);
  1520.             var subLine = line.substr(0, wrapIndex);
  1521.             line = line.substr(wrapIndex);
  1522.             html.push(noEscapeHTML ? subLine : escapeHTML(subLine));
  1523.         }
  1524.         html.push(noEscapeHTML ? line : escapeHTML(line));
  1525.     }
  1526.  
  1527.     return html.join("\n");
  1528. }
  1529.  
  1530. this.insertWrappedText = function(text, textBox, noEscapeHTML)
  1531. {
  1532.     textBox.innerHTML = "<pre>" + this.wrapText(text, noEscapeHTML) + "</pre>";
  1533. }
  1534.  
  1535. // ************************************************************************************************
  1536. // Menus
  1537.  
  1538. this.createMenu = function(popup, label)
  1539. {
  1540.     var menu = popup.ownerDocument.createElement("menu");
  1541.     menu.setAttribute("label", label);
  1542.  
  1543.     var menuPopup = popup.ownerDocument.createElement("menupopup");
  1544.  
  1545.     popup.appendChild(menu);
  1546.     menu.appendChild(menuPopup);
  1547.  
  1548.     return menuPopup;
  1549. };
  1550.  
  1551. this.createMenuItem = function(popup, item, before)
  1552. {
  1553.     if (typeof(item) == "string" && item.indexOf("-") == 0)
  1554.         return this.createMenuSeparator(popup, before);
  1555.  
  1556.     var menuitem = popup.ownerDocument.createElement("menuitem");
  1557.  
  1558.     this.setItemIntoElement(menuitem, item);
  1559.  
  1560.     if (before)
  1561.         popup.insertBefore(menuitem, before);
  1562.     else
  1563.         popup.appendChild(menuitem);
  1564.     return menuitem;
  1565. };
  1566.  
  1567. this.setItemIntoElement = function(element, item)
  1568. {
  1569.     var label = item.nol10n ? item.label : this.$STR(item.label);
  1570.  
  1571.     element.setAttribute("label", label);
  1572.     element.setAttribute("type", item.type);
  1573.     if (item.checked)
  1574.         element.setAttribute("checked", "true");
  1575.     if (item.disabled)
  1576.         element.setAttribute("disabled", "true");
  1577.     if (item.image)
  1578.     {
  1579.         element.setAttribute("class", "element-iconic");
  1580.         element.setAttribute("image", item.image);
  1581.     }
  1582.  
  1583.     if (item.command)
  1584.         element.addEventListener("command", item.command, false);
  1585.  
  1586.     return element;
  1587. }
  1588.  
  1589.  
  1590. this.createMenuHeader = function(popup, item)
  1591. {
  1592.     var header = popup.ownerDocument.createElement("label");
  1593.     header.setAttribute("class", "menuHeader");
  1594.  
  1595.     var label = item.nol10n ? item.label : this.$STR(item.label);
  1596.  
  1597.     header.setAttribute("value", label);
  1598.  
  1599.     popup.appendChild(header);
  1600.     return header;
  1601. };
  1602.  
  1603. this.createMenuSeparator = function(popup, before)
  1604. {
  1605.     if (!popup.firstChild)
  1606.         return;
  1607.  
  1608.     var menuitem = popup.ownerDocument.createElement("menuseparator");
  1609.     if (before)
  1610.         popup.insertBefore(menuitem, before);
  1611.     else
  1612.         popup.appendChild(menuitem);
  1613.     return menuitem;
  1614. };
  1615.  
  1616. this.optionMenu = function(label, option)
  1617. {
  1618.     return {label: label, type: "checkbox", checked: Firebug[option],
  1619.         command: this.bindFixed(Firebug.setPref, Firebug, Firebug.prefDomain, option, !Firebug[option]) };
  1620. };
  1621.  
  1622. this.serviceOptionMenu = function(label, option)
  1623. {
  1624.     return {label: label, type: "checkbox", checked: Firebug[option],
  1625.         command: this.bindFixed(Firebug.setPref, Firebug, Firebug.servicePrefDomain, option, !Firebug[option]) };
  1626. };
  1627.  
  1628. // ************************************************************************************************
  1629. // Stack Traces
  1630.  
  1631. this.getCurrentStackTrace = function(context)
  1632. {
  1633.     var trace = null;
  1634.  
  1635.     Firebug.Debugger.halt(function(frame)
  1636.     {
  1637.         trace = FBL.getStackTrace(frame, context);
  1638.     });
  1639.  
  1640.     return trace;
  1641. };
  1642.  
  1643. this.getStackTrace = function(frame, context)
  1644. {
  1645.     var trace = new this.StackTrace();
  1646.  
  1647.     for (; frame && frame.isValid; frame = frame.callingFrame)
  1648.     {
  1649.         if (!(Firebug.filterSystemURLs && this.isSystemURL(FBL.normalizeURL(frame.script.fileName))))
  1650.         {
  1651.             var stackFrame = this.getStackFrame(frame, context);
  1652.             if (stackFrame)
  1653.                 trace.frames.push(stackFrame);
  1654.         }
  1655.         else
  1656.         {                                                                                                           /*@explore*/
  1657.         }                                                                                                           /*@explore*/
  1658.     }
  1659.  
  1660.     if (trace.frames.length > 100)
  1661.     {
  1662.         var originalLength = trace.frames.length;
  1663.         trace.frames.splice(50, originalLength - 100);
  1664.         var excuse = "(eliding "+(originalLength - 100)+" frames)";
  1665.         trace.frames[50] = new this.StackFrame(context, excuse, null, excuse, 0, []);
  1666.     }
  1667.  
  1668.     return trace;
  1669. };
  1670.  
  1671. this.getStackFrame = function(frame, context)
  1672. {
  1673.     if (frame.isNative || frame.isDebugger)
  1674.     {
  1675.         var excuse = (frame.isNative) ?  "(native)" : "(debugger)";
  1676.         return new this.StackFrame(context, excuse, null, excuse, 0, []);
  1677.     }
  1678.     try
  1679.     {
  1680.         var sourceFile = this.getSourceFileByScript(context, frame.script);
  1681.         if (sourceFile)
  1682.         {
  1683.             var url = sourceFile.href;
  1684.             var analyzer = sourceFile.getScriptAnalyzer(frame.script);
  1685.  
  1686.             var lineNo = analyzer.getSourceLineFromFrame(context, frame);
  1687.             var fncSpec = analyzer.getFunctionDescription(frame.script, context, frame);
  1688.             if (!fncSpec.name)
  1689.                 fncSpec.name = frame.script.functionName;
  1690.             
  1691.             return new this.StackFrame(context, fncSpec.name, frame.script, url, lineNo, fncSpec.args, frame.pc);
  1692.         }
  1693.         else
  1694.         {
  1695.             var script = frame.script;
  1696.  
  1697.             return new this.StackFrame(context, script.functionName, frame.script, FBL.normalizeURL(script.fileName), frame.line, [], frame.pc);
  1698.         }
  1699.     }
  1700.     catch (exc)
  1701.     {
  1702.         return null;
  1703.     }
  1704. };
  1705.  
  1706. this.getStackDump = function()
  1707. {
  1708.     var lines = [];
  1709.     for (var frame = Components.stack; frame; frame = frame.caller)
  1710.         lines.push(frame.filename + " (" + frame.lineNumber + ")");
  1711.  
  1712.     return lines.join("\n");
  1713. };
  1714.  
  1715. this.getStackSourceLink = function()
  1716. {
  1717.     for (var frame = Components.stack; frame; frame = frame.caller)
  1718.     {
  1719.         if (frame.filename && frame.filename.indexOf("chrome://firebug/") == 0)
  1720.         {
  1721.             for (; frame; frame = frame.caller)
  1722.             {
  1723.                 var firebugComponent = "/components/firebug-";
  1724.                 if (frame.filename && frame.filename.indexOf("chrome://firebug/") != 0 &&
  1725.                     frame.filename.indexOf(firebugComponent) == -1)
  1726.                     break;
  1727.             }
  1728.             break;
  1729.         }
  1730.     }
  1731.     return this.getFrameSourceLink(frame);
  1732. }
  1733.  
  1734. this.getFrameSourceLink = function(frame)
  1735. {
  1736.     if (frame && frame.filename && frame.filename.indexOf("XPCSafeJSObjectWrapper") == -1)
  1737.         return new FBL.SourceLink(frame.filename, frame.lineNumber, "js");
  1738.     else
  1739.         return null;
  1740. };
  1741.  
  1742. this.getStackFrameId = function()
  1743. {
  1744.     for (var frame = Components.stack; frame; frame = frame.caller)
  1745.     {
  1746.         if (frame.languageName == "JavaScript"
  1747.             && !(frame.filename && frame.filename.indexOf("chrome://firebug/") == 0))
  1748.         {
  1749.             return frame.filename + "/" + frame.lineNumber;
  1750.         }
  1751.     }
  1752.     return null;
  1753. };
  1754.  
  1755. // ************************************************************************************************
  1756. // Event Monitoring
  1757.  
  1758. this.toggleMonitorEvents = function(object, type, state, context)
  1759. {
  1760.     if (state)
  1761.         this.unmonitorEvents(object, type, context);
  1762.     else
  1763.         this.monitorEvents(object, type, context);
  1764. };
  1765.  
  1766. this.monitorEvents = function(object, type, context)
  1767. {
  1768.     if (!this.areEventsMonitored(object, type, context) && object && object.addEventListener)
  1769.     {
  1770.         if (!context.onMonitorEvent)
  1771.             context.onMonitorEvent = function(event) { Firebug.Console.log(event, context); };
  1772.  
  1773.         if (!context.eventsMonitored)
  1774.             context.eventsMonitored = [];
  1775.  
  1776.         context.eventsMonitored.push({object: object, type: type});
  1777.  
  1778.         if (!type)
  1779.             this.attachAllListeners(object, context.onMonitorEvent, context);
  1780.         else
  1781.             object.addEventListener(type, context.onMonitorEvent, false);
  1782.     }
  1783. };
  1784.  
  1785. this.unmonitorEvents = function(object, type, context)
  1786. {
  1787.     var eventsMonitored = context.eventsMonitored;
  1788.  
  1789.     for (var i = 0; i < eventsMonitored.length; ++i)
  1790.     {
  1791.         if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
  1792.         {
  1793.             eventsMonitored.splice(i, 1);
  1794.  
  1795.             if (!type)
  1796.                 this.detachAllListeners(object, context.onMonitorEvent, context);
  1797.             else
  1798.                 object.removeEventListener(type, context.onMonitorEvent, false);
  1799.             break;
  1800.         }
  1801.     }
  1802. };
  1803.  
  1804. this.areEventsMonitored = function(object, type, context)
  1805. {
  1806.     var eventsMonitored = context.eventsMonitored;
  1807.     if (eventsMonitored)
  1808.     {
  1809.         for (var i = 0; i < eventsMonitored.length; ++i)
  1810.         {
  1811.             if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
  1812.                 return true;
  1813.         }
  1814.     }
  1815.  
  1816.     return false;
  1817. };
  1818.  
  1819. // ************************************************************************************************
  1820. // Functions
  1821.  
  1822. this.findScripts = function(context, url, line)
  1823. {
  1824.     var sourceFile = context.sourceFileMap[url];
  1825.     if (sourceFile)
  1826.         var scripts = sourceFile.scriptsIfLineCouldBeExecutable(line);
  1827.     else
  1828.     {
  1829.     }
  1830.     return scripts;
  1831. };
  1832.  
  1833. this.findScriptForFunctionInContext = function(context, fn)
  1834. {
  1835.     var found = null;
  1836.  
  1837.     for (var url in context.sourceFileMap)
  1838.     {
  1839.         var sourceFile = context.sourceFileMap[url];
  1840.         sourceFile.forEachScript(function seekFn(script)
  1841.         {
  1842.             if (!script.isValid)
  1843.                 return;
  1844.             try
  1845.             {
  1846.                 var testFunctionObject = script.functionObject;
  1847.                 if (!testFunctionObject.isValid)
  1848.                     return;
  1849.                 var unwrapped = testFunctionObject.getWrappedValue();
  1850.                 if (!unwrapped.toString)
  1851.                     return;
  1852.                 try {
  1853.                     var tfs = unwrapped.toString();
  1854.                 } catch (etfs) {
  1855.                     FBTrace.dumpProperties("unwrapped.toString fails for unwrapped: "+etfs, unwrapped);
  1856.                 }
  1857.  
  1858.                 if (!fn.toString)
  1859.                     return;
  1860.  
  1861.                 var fns = fn.toString();
  1862.                 if (tfs == fns)
  1863.                     found = script;
  1864.             }
  1865.             catch(exc)
  1866.             {
  1867.             }
  1868.         });
  1869.     }
  1870.  
  1871.     return found;
  1872. }
  1873.  
  1874. this.findScriptForFunction = function(fn)
  1875. {
  1876.     var found = {tag: "not set"};
  1877.  
  1878.     this.jsd.enumerateScripts({enumerateScript: function findScriptMatchingFn(script)
  1879.     {
  1880.         try {
  1881.             if (script.isValid)
  1882.             {
  1883.  
  1884.                 var iValueFunctionObject = script.functionObject;
  1885.                 //FBTrace.dumpIValue("lib.findScriptForFunction iValueFunctionObject", iValueFunctionObject);
  1886.                 var testFunctionObject = script.functionObject.getWrappedValue();
  1887.                 if (testFunctionObject instanceof Function)
  1888.                     FBTrace.sysout("lib.findScriptForFunction testFunctionObject "+testFunctionObject+" vs "+fn+"\n");
  1889.                 if (testFunctionObject == fn)
  1890.                 {
  1891.                     found = script;
  1892.                     return;
  1893.                 }
  1894.             }
  1895.         } catch (exc) {
  1896.         }
  1897.     }});
  1898.  
  1899.     FBTrace.dumpProperties("findScriptForFunction found ", found.tag);
  1900.     return found;
  1901. };
  1902.  
  1903. this.findSourceForFunction = function(fn, context)
  1904. {
  1905.     var script = this.findScriptForFunctionInContext(context, fn);
  1906.     return (script)? this.getSourceLinkForScript(script, context) : null;
  1907. };
  1908.  
  1909. this.getSourceLinkForScript = function(script, context)
  1910. {
  1911.     var sourceFile = this.getSourceFileByScript(context, script);
  1912.     if (sourceFile)
  1913.     {
  1914.         var scriptAnalyzer = sourceFile.getScriptAnalyzer(script);
  1915.         return scriptAnalyzer.getSourceLinkForScript(script);
  1916.     }
  1917. };
  1918.  
  1919. this.getFunctionName = function(script, context, frame)
  1920. {
  1921.     if (!script)
  1922.     {
  1923.         return "(no script)";
  1924.     }
  1925.     var name = script.functionName;
  1926.  
  1927.     if (!name || (name == "anonymous"))
  1928.     {
  1929.         var analyzer = FBL.getScriptAnalyzer(context, script);
  1930.         if (analyzer)
  1931.         {
  1932.             var functionSpec = analyzer.getFunctionDescription(script, context, frame);
  1933.             name = functionSpec.name +"("+functionSpec.args.join(',')+")";
  1934.         }
  1935.         else
  1936.         {
  1937.             name =  this.guessFunctionName(FBL.normalizeURL(script.fileName), script.baseLineNumber, context);
  1938.         }
  1939.     }
  1940.     return name;
  1941. };
  1942.  
  1943. this.guessFunctionName = function(url, lineNo, context)
  1944. {
  1945.     if (context)
  1946.     {
  1947.         if (context.sourceCache)
  1948.             return this.guessFunctionNameFromLines(url, lineNo, context.sourceCache);
  1949.     }
  1950.     return "? in "+this.getFileName(url)+"@"+lineNo;
  1951. };
  1952.  
  1953. this.guessFunctionNameFromLines = function(url, lineNo, sourceCache) {
  1954.         // Walk backwards from the first line in the function until we find the line which
  1955.         // matches the pattern above, which is the function definition
  1956.         var line = "";
  1957.         for (var i = 0; i < 4; ++i)
  1958.         {
  1959.             line = sourceCache.getLine(url, lineNo-i) + line;
  1960.             if (line != undefined)
  1961.             {
  1962.                 var m = reGuessFunction.exec(line);
  1963.                 if (m)
  1964.                     return m[1];
  1965.                 else
  1966.                 m = reFunctionArgNames.exec(line);
  1967.                 if (m && m[1])
  1968.                     return m[1];
  1969.             }
  1970.         }
  1971.         return "(?)";
  1972. };
  1973.  
  1974. this.getFunctionArgNames = function(fn)
  1975. {
  1976.     var m = reFunctionArgNames.exec(this.safeToString(fn));
  1977.     if (m)
  1978.     {
  1979.         var argNames = m[2].split(", ");
  1980.         if (argNames.length && argNames[0])
  1981.             return argNames;
  1982.     }
  1983.     return [];
  1984. };
  1985.  
  1986. this.getFunctionArgValues = function(fn, frame)
  1987. {
  1988.     var values = [];
  1989.  
  1990.     var argNames = this.getFunctionArgNames(fn);
  1991.     for (var i = 0; i < argNames.length; ++i)
  1992.     {
  1993.         var argName = argNames[i];
  1994.         var pvalue = frame.scope.getProperty(argName);
  1995.         var value = pvalue ? pvalue.value.getWrappedValue() : undefined;
  1996.         values.push({name: argName, value: value});
  1997.     }
  1998.  
  1999.     return values;
  2000. };
  2001.  
  2002. // ************************************************************************************************
  2003. // Source Files
  2004.  
  2005. this.getScriptFileByHref = function(url, context)
  2006. {
  2007.     return context.sourceFileMap[url];
  2008. };
  2009.  
  2010. this.getStyleSheetByHref = function(url, context)
  2011. {
  2012.     function addSheet(sheet)
  2013.     {
  2014.         if (sheet.href == null) //http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null.
  2015.             return sheet;
  2016.  
  2017.         for (var i = 0; i < sheet.cssRules.length; ++i)
  2018.         {
  2019.             var rule = sheet.cssRules[i];
  2020.             if (rule instanceof CSSImportRule)
  2021.             {
  2022.                 var found = addSheet(rule.styleSheet);
  2023.                 if (found)
  2024.                     return found;
  2025.             }
  2026.         }
  2027.     }
  2028.  
  2029.     var rootSheets = context.window.document.styleSheets;
  2030.     for (var i = 0; i < rootSheets.length; ++i)
  2031.     {
  2032.         var found = addSheet(rootSheets[i]);
  2033.         if (found)
  2034.             return found;
  2035.     }
  2036. };
  2037.  
  2038. this.sourceFilesAsArray = function(context)
  2039. {
  2040.     var sourceFiles = [];
  2041.     var sourceFileMap = context.sourceFileMap;
  2042.     for (var url in sourceFileMap)
  2043.         sourceFiles.push(sourceFileMap[url]);
  2044.     return sourceFiles;
  2045. };
  2046.  
  2047. this.updateScriptFiles = function(context, eraseSourceFileMap)  // scan windows for 'script' tags
  2048. {
  2049.     var oldMap = eraseSourceFileMap ? null : context.sourceFileMap;
  2050.  
  2051.     function addFile(url, scriptTagNumber, dependentURL)
  2052.     {
  2053.             if (oldMap && url in oldMap)
  2054.             {
  2055.                 var sourceFile = oldMap[url];
  2056.                 sourceFile.dependentURL = dependentURL;
  2057.                 context.sourceFileMap[url] = sourceFile;
  2058.                 return false;
  2059.             }
  2060.             else
  2061.             {
  2062.                 var sourceFile = new FBL.ScriptTagSourceFile(context, url, scriptTagNumber);
  2063.                 sourceFile.dependentURL = dependentURL;
  2064.                 context.sourceFileMap[url] = sourceFile;
  2065.                 return true;
  2066.             }
  2067.     }
  2068.  
  2069.     this.iterateWindows(context.window, this.bind(function(win)
  2070.     {
  2071.         if (!win.document.documentElement)
  2072.             return;
  2073.  
  2074.         var baseUrl = win.location.href;
  2075.         var bases = win.document.documentElement.getElementsByTagName("base");
  2076.         if (bases && bases[0])
  2077.         {
  2078.             baseUrl = bases[0].href;
  2079.         }
  2080.  
  2081.         var scripts = win.document.documentElement.getElementsByTagName("script");
  2082.         for (var i = 0; i < scripts.length; ++i)
  2083.         {
  2084.             var scriptSrc = scripts[i].getAttribute('src'); // for XUL use attribute
  2085.             var url = scriptSrc ? this.absoluteURL(scriptSrc, baseUrl) : win.location.href;
  2086.             url = this.normalizeURL(url ? url : win.location.href);
  2087.             var added = addFile(url, i, (scriptSrc?win.location.href:null));
  2088.         }
  2089.     }, this));
  2090. };
  2091.  
  2092. // ************************************************************************************************
  2093. // Firefox browsing
  2094.  
  2095. this.openNewTab = function(url)
  2096. {
  2097.     if (url)
  2098.         gBrowser.selectedTab = gBrowser.addTab(url);
  2099. };
  2100.  
  2101. this.openWindow = function(windowType, url, features, params)
  2102. {
  2103.     var wm = this.CCSV("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
  2104.  
  2105.     var win = windowType ? wm.getMostRecentWindow(windowType) : null;
  2106.     if (win) {
  2107.       if ("initWithParams" in win)
  2108.         win.initWithParams(params);
  2109.       win.focus();
  2110.     }
  2111.     else {
  2112.       var winFeatures = "resizable,dialog=no,centerscreen" + (features != "" ? ("," + features) : "");
  2113.       var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener;
  2114.       win = parentWindow.openDialog(url, "_blank", winFeatures, params);
  2115.     }
  2116.     return win;
  2117. };
  2118.  
  2119. this.viewSource = function(url, lineNo)
  2120. {
  2121.     window.openDialog("chrome://global/content/viewSource.xul", "_blank",
  2122.         "all,dialog=no", url, null, null, lineNo);
  2123. };
  2124.  
  2125. // Iterate over all opened firefox windows of the given type. If the callback returns true
  2126. // the iteration is stopped.
  2127. this.iterateBrowserWindows = function(windowType, callback)
  2128. {
  2129.     var windowList = wm.getZOrderDOMWindowEnumerator(windowType, true);
  2130.     if (!windowList.hasMoreElements())
  2131.         windowList = wm.getEnumerator(windowType);
  2132.  
  2133.     while (windowList.hasMoreElements()) {
  2134.         if (callback(windowList.getNext()))
  2135.             return true;
  2136.     }
  2137.  
  2138.     return false;
  2139. };
  2140.  
  2141. // ************************************************************************************************
  2142. // JavaScript Parsing
  2143.  
  2144. this.getExpressionAt = function(text, charOffset)
  2145. {
  2146.     var offset = 0;
  2147.     for (var m = reWord.exec(text); m; m = reWord.exec(text.substr(offset)))
  2148.     {
  2149.         var word = m[0];
  2150.         var wordOffset = offset+m.index;
  2151.         if (charOffset >= wordOffset && charOffset <= wordOffset+word.length)
  2152.         {
  2153.             var innerOffset = charOffset-wordOffset;
  2154.             var dots = word.substr(0, innerOffset).split(".").length;
  2155.             var subExpr = word.split(".").slice(0, dots).join(".");
  2156.             return {expr: subExpr, offset: wordOffset};
  2157.         }
  2158.  
  2159.         offset = wordOffset+word.length;
  2160.     }
  2161.  
  2162.     return {expr: null, offset: -1};
  2163. };
  2164.  
  2165. var jsKeywords =
  2166. {
  2167.     "var": 1,
  2168.     "const": 1,
  2169.     "class": 1,
  2170.     "extends": 1,
  2171.     "import": 1,
  2172.     "namespace": 1,
  2173.     "function": 1,
  2174.     "debugger": 1,
  2175.     "new": 1,
  2176.     "delete": 1,
  2177.     "null": 1,
  2178.     "undefined": 1,
  2179.     "true": 1,
  2180.     "false": 1,
  2181.     "void": 1,
  2182.     "typeof": 1,
  2183.     "instanceof": 1,
  2184.     "break": 1,
  2185.     "continue": 1,
  2186.     "return": 1,
  2187.     "throw": 1,
  2188.     "try": 1,
  2189.     "catch": 1,
  2190.     "finally": 1,
  2191.     "if": 1,
  2192.     "else": 1,
  2193.     "for": 1,
  2194.     "while": 1,
  2195.     "do": 1,
  2196.     "with": 1,
  2197.     "switch": 1,
  2198.     "case": 1,
  2199.     "default": 1
  2200. };
  2201.  
  2202. this.isJavaScriptKeyword = function(name)
  2203. {
  2204.     return name in jsKeywords;
  2205. };
  2206.  
  2207. // ************************************************************************************************
  2208. // Events
  2209.  
  2210. this.cancelEvent = function(event)
  2211. {
  2212.     event.stopPropagation();
  2213.     event.preventDefault();
  2214. };
  2215.  
  2216. this.isLeftClick = function(event)
  2217. {
  2218.     return event.button == 0 && this.noKeyModifiers(event);
  2219. };
  2220.  
  2221. this.isMiddleClick = function(event)
  2222. {
  2223.     return event.button == 1 && this.noKeyModifiers(event);
  2224. };
  2225.  
  2226. this.isRightClick = function(event)
  2227. {
  2228.     return event.button == 2 && this.noKeyModifiers(event);
  2229. };
  2230.  
  2231. this.noKeyModifiers = function(event)
  2232. {
  2233.     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
  2234. };
  2235.  
  2236. this.isControlClick = function(event)
  2237. {
  2238.     return event.button == 0 && this.isControl(event);
  2239. };
  2240.  
  2241. this.isShiftClick = function(event)
  2242. {
  2243.     return event.button == 0 && this.isShift(event);
  2244. };
  2245.  
  2246. this.isControl = function(event)
  2247. {
  2248.     return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
  2249. };
  2250.  
  2251. this.isControlShift = function(event)
  2252. {
  2253.     return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
  2254. };
  2255.  
  2256. this.isShift = function(event)
  2257. {
  2258.     return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
  2259. };
  2260.  
  2261. this.dispatch = function(listeners, name, args)
  2262. {
  2263.     try {
  2264.         for (var i = 0; i < listeners.length; ++i)
  2265.         {
  2266.             var listener = listeners[i];
  2267.             if ( listener.hasOwnProperty(name) )
  2268.                 listener[name].apply(listener, args);
  2269.         }
  2270.     }
  2271.     catch (exc)
  2272.     {
  2273.     }
  2274. };
  2275.  
  2276. this.dispatch2 = function(listeners, name, args)
  2277. {
  2278.     for (var i = 0; i < listeners.length; ++i)
  2279.     {
  2280.         var listener = listeners[i];
  2281.         if ( listener.hasOwnProperty(name) )
  2282.         {
  2283.             var result = listener[name].apply(listener, args);
  2284.             if ( result )
  2285.                 return result;
  2286.         }
  2287.     }
  2288. };
  2289.  
  2290. // ************************************************************************************************
  2291. // DOM Events
  2292.  
  2293. const eventTypes =
  2294. {
  2295.     composition: [
  2296.         "composition",
  2297.         "compositionstart",
  2298.         "compositionend" ],
  2299.     contextmenu: [
  2300.         "contextmenu" ],
  2301.     drag: [
  2302.         "dragenter",
  2303.         "dragover",
  2304.         "dragexit",
  2305.         "dragdrop",
  2306.         "draggesture" ],
  2307.     focus: [
  2308.         "focus",
  2309.         "blur" ],
  2310.     form: [
  2311.         "submit",
  2312.         "reset",
  2313.         "change",
  2314.         "select",
  2315.         "input" ],
  2316.     key: [
  2317.         "keydown",
  2318.         "keyup",
  2319.         "keypress" ],
  2320.     load: [
  2321.         "load",
  2322.         "beforeunload",
  2323.         "unload",
  2324.         "abort",
  2325.         "error" ],
  2326.     mouse: [
  2327.         "mousedown",
  2328.         "mouseup",
  2329.         "click",
  2330.         "dblclick",
  2331.         "mouseover",
  2332.         "mouseout",
  2333.         "mousemove" ],
  2334.     mutation: [
  2335.         "DOMSubtreeModified",
  2336.         "DOMNodeInserted",
  2337.         "DOMNodeRemoved",
  2338.         "DOMNodeRemovedFromDocument",
  2339.         "DOMNodeInsertedIntoDocument",
  2340.         "DOMAttrModified",
  2341.         "DOMCharacterDataModified" ],
  2342.     paint: [
  2343.         "paint",
  2344.         "resize",
  2345.         "scroll" ],
  2346.     scroll: [
  2347.         "overflow",
  2348.         "underflow",
  2349.         "overflowchanged" ],
  2350.     text: [
  2351.         "text" ],
  2352.     ui: [
  2353.         "DOMActivate",
  2354.         "DOMFocusIn",
  2355.         "DOMFocusOut" ],
  2356.     xul: [
  2357.         "popupshowing",
  2358.         "popupshown",
  2359.         "popuphiding",
  2360.         "popuphidden",
  2361.         "close",
  2362.         "command",
  2363.         "broadcast",
  2364.         "commandupdate" ]
  2365. };
  2366.  
  2367. this.getEventFamily = function(eventType)
  2368. {
  2369.     if (!this.families)
  2370.     {
  2371.         this.families = {};
  2372.  
  2373.         for (var family in eventTypes)
  2374.         {
  2375.             var types = eventTypes[family];
  2376.             for (var i = 0; i < types.length; ++i)
  2377.                 this.families[types[i]] = family;
  2378.         }
  2379.     }
  2380.  
  2381.     return this.families[eventType];
  2382. };
  2383.  
  2384. this.attachAllListeners = function(object, listener)
  2385. {
  2386.     for (var family in eventTypes)
  2387.     {
  2388.         if (family != "mutation" || Firebug.attachMutationEvents)
  2389.             this.attachFamilyListeners(family, object, listener);
  2390.     }
  2391. };
  2392.  
  2393. this.detachAllListeners = function(object, listener)
  2394. {
  2395.     for (var family in eventTypes)
  2396.     {
  2397.         if (family != "mutation" || Firebug.attachMutationEvents)
  2398.             this.detachFamilyListeners(family, object, listener);
  2399.     }
  2400. };
  2401.  
  2402. this.attachFamilyListeners = function(family, object, listener)
  2403. {
  2404.     var types = eventTypes[family];
  2405.     for (var i = 0; i < types.length; ++i)
  2406.         object.addEventListener(types[i], listener, false);
  2407. };
  2408.  
  2409. this.detachFamilyListeners = function(family, object, listener)
  2410. {
  2411.     var types = eventTypes[family];
  2412.     for (var i = 0; i < types.length; ++i)
  2413.         object.removeEventListener(types[i], listener, false);
  2414. };
  2415.  
  2416. // ************************************************************************************************
  2417. // URLs
  2418.  
  2419. this.getFileName = function(url)
  2420. {
  2421.     var split = this.splitURLBase(url);
  2422.     return split.name;
  2423. };
  2424.  
  2425. this.splitFileName = function(url)
  2426. { // Dead code
  2427.     var d = this.reDataURL.exec(url);
  2428.     if (d)
  2429.     {
  2430.         var path = decodeURIComponent(d[1]);
  2431.         if (!d[2])
  2432.             return { path: path, name: 'eval' };
  2433.         else
  2434.             return { path: path, name: 'eval', line: d[2] };
  2435.     }
  2436.  
  2437.     var m = reSplitFile.exec(url);
  2438.     if (!m)
  2439.         return {name: url, path: url};
  2440.     else if (!m[2])
  2441.         return {path: m[1], name: m[1]};
  2442.     else
  2443.         return {path: m[1], name: m[2]};
  2444. };
  2445.  
  2446. this.splitURLBase = function(url)
  2447. {
  2448.     if (this.isDataURL(url))
  2449.         return this.splitDataURL(url);
  2450.     return this.splitURLTrue(url);
  2451. };
  2452.  
  2453. this.splitDataURL = function(url)
  2454. {
  2455.     var mark = url.indexOf(':', 3);
  2456.     if (mark != 4)
  2457.         return false;    //  the first 5 chars must be 'data:'
  2458.     
  2459.     var point = url.indexOf(',', mark+1);
  2460.     if (point < mark)
  2461.         return false; // syntax error
  2462.     
  2463.     var props = { encodedContent: url.substr(point+1) };
  2464.     
  2465.     var metadataBuffer = url.substr(mark+1, point);
  2466.     var metadata = metadataBuffer.split(';');
  2467.     for (var i = 0; i < metadata.length; i++)
  2468.     {
  2469.         var nv = metadata[i].split('=');
  2470.         if (nv.length == 2)
  2471.             props[nv[0]] = nv[1];
  2472.     }
  2473.     
  2474.     // Additional Firebug-specific properties
  2475.     if (props.hasOwnProperty('fileName')) 
  2476.     {
  2477.          var caller_URL = decodeURIComponent(props['fileName']);
  2478.          var caller_split = this.splitURLTrue(caller_URL);
  2479.  
  2480.         if (props.hasOwnProperty('baseLineNumber'))  // this means it's probably an eval()
  2481.         {
  2482.             props['path'] = caller_split.path;
  2483.             props['line'] = props['baseLineNumber'];
  2484.             var hint = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, "");
  2485.             props['name'] =  'eval->'+hint;
  2486.         }
  2487.         else
  2488.         {
  2489.             props['name'] = caller_split.name;
  2490.             props['path'] = caller_split.path;
  2491.         }
  2492.     }
  2493.     
  2494.     return props;
  2495. };
  2496.  
  2497. this.splitURLTrue = function(url)
  2498. {
  2499.     var m = reSplitFile.exec(url);
  2500.     if (!m)
  2501.         return {name: url, path: url};
  2502.     else if (!m[2])
  2503.         return {path: m[1], name: m[1]};
  2504.     else
  2505.         return {path: m[1], name: m[2]+m[3]};
  2506. };
  2507.  
  2508. this.getFileExtension = function(url)
  2509. {
  2510.     var lastDot = url.lastIndexOf(".");
  2511.     return url.substr(lastDot+1);
  2512. };
  2513.  
  2514. this.isSystemURL = function(url)
  2515. {
  2516.     if (!url) return true;
  2517.     if (url.length == 0) return true;
  2518.     if (url[0] == 'h') return false;
  2519.     if (url.substr(0, 9) == "resource:")
  2520.         return true;
  2521.     else if (url.substr(0, 17) == "chrome://firebug/")
  2522.         return true;
  2523.     else if (url  == "XPCSafeJSObjectWrapper.cpp")
  2524.         return true;
  2525.     else if (url.substr(0, 6) == "about:")
  2526.         return true;
  2527.     else if (url.indexOf("firebug-service.js") != -1)
  2528.         return true;
  2529.     else
  2530.         return false;
  2531. };
  2532.  
  2533. this.isSystemPage = function(win)
  2534. {
  2535.     try
  2536.     {
  2537.         var doc = win.document;
  2538.         if (!doc)
  2539.             return false;
  2540.  
  2541.         // Detect pages for pretty printed XML
  2542.         if ((doc.styleSheets.length && doc.styleSheets[0].href
  2543.                 == "chrome://global/content/xml/XMLPrettyPrint.css")
  2544.             || (doc.styleSheets.length > 1 && doc.styleSheets[1].href
  2545.                 == "chrome://browser/skin/feeds/subscribe.css"))
  2546.             return true;
  2547.  
  2548.         return FBL.isSystemURL(win.location.href);
  2549.     }
  2550.     catch (exc)
  2551.     {
  2552.         // Sometimes documents just aren't ready to be manipulated here, but don't let that
  2553.         // gum up the works
  2554.         ERROR("tabWatcher.isSystemPage document not ready:"+ exc);
  2555.         return false;
  2556.     }
  2557. }
  2558.  
  2559. this.getURIHost = function(uri)
  2560. {
  2561.     try
  2562.     {
  2563.         if (uri)
  2564.             return uri.host;
  2565.         else
  2566.             return "";
  2567.     }
  2568.     catch (exc)
  2569.     {
  2570.         return "";
  2571.     }
  2572. }
  2573.  
  2574. this.isLocalURL = function(url)
  2575. {
  2576.     if (url.substr(0, 5) == "file:")
  2577.         return true;
  2578.     else if (url.substr(0, 8) == "wyciwyg:")
  2579.         return true;
  2580.     else
  2581.         return false;
  2582. };
  2583.  
  2584. this.isDataURL = function(url)
  2585. {
  2586.     return (url && url.substr(0,5) == "data:");
  2587. };
  2588.  
  2589. this.getLocalPath = function(url)
  2590. {
  2591.     if (this.isLocalURL(url))
  2592.     {
  2593.         var ioService = this.CCSV("@mozilla.org/network/io-service;1", "nsIIOService");
  2594.         var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  2595.         var file = fileHandler.getFileFromURLSpec(url);
  2596.         return file.path;
  2597.     }
  2598. };
  2599.  
  2600. this.getURLFromLocalFile = function(file)
  2601. {
  2602.     var ioService = this.CCSV("@mozilla.org/network/io-service;1", "nsIIOService");  // TODO cache?
  2603.     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  2604.     var URL = fileHandler.getURLSpecFromFile(file);
  2605.     return URL;
  2606. };
  2607.  
  2608. this.getDataURLForContent = function(content, url)
  2609. {
  2610.     // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
  2611.     var uri = "data:text/html;";
  2612.     uri += "fileName="+encodeURIComponent(url)+ ","
  2613.     uri += encodeURIComponent(content);
  2614.     return uri;
  2615. },
  2616.  
  2617. this.getDomain = function(url)
  2618. {
  2619.     var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
  2620.     return m ? m[1] : "";
  2621. };
  2622.  
  2623. this.getURLPath = function(url)
  2624. {
  2625.     var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
  2626.     return m ? m[1] : "";
  2627. };
  2628.  
  2629. this.getPrettyDomain = function(url)
  2630. {
  2631.     var m = /[^:]+:\/{1,3}(www.)?([^\/]+)/.exec(url);
  2632.     return m ? m[2] : "";
  2633. };
  2634.  
  2635. this.absoluteURL = function(url, baseURL)
  2636. {
  2637.     return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
  2638. };
  2639.  
  2640. this.absoluteURLWithDots = function(url, baseURL)
  2641. {
  2642.     if (url[0] == "?")
  2643.         return baseURL + url;
  2644.  
  2645.     var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;
  2646.     var m = reURL.exec(url);
  2647.     if (m)
  2648.         return url;
  2649.  
  2650.     var m = reURL.exec(baseURL);
  2651.     if (!m)
  2652.         return "";
  2653.  
  2654.     var head = m[1];
  2655.     var tail = m[3];
  2656.     if (url.substr(0, 2) == "//")
  2657.         return m[2] + url;
  2658.     else if (url[0] == "/")
  2659.     {
  2660.         return head + url;
  2661.     }
  2662.     else if (tail[tail.length-1] == "/")
  2663.         return baseURL + url;
  2664.     else
  2665.     {
  2666.         var parts = tail.split("/");
  2667.         return head + parts.slice(0, parts.length-1).join("/") + "/" + url;
  2668.     }
  2669. }
  2670.  
  2671. this.normalizeURL = function(url)
  2672. {
  2673.     if (!url)
  2674.         return "";
  2675.     // Replace one or more characters that are not forward-slash followed by /.., by space.
  2676.     if (url.length < 255) // guard against monsters.
  2677.         url = url.replace(/[^/]+\/\.\.\//, "", "g");
  2678.     // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they
  2679.     // don't match up with the URLs we get back from the DOM
  2680.     return url ? url.replace(/file:\/([^/])/g, "file:///$1") : "";
  2681. };
  2682.  
  2683. this.denormalizeURL = function(url)
  2684. {
  2685.     return url.replace(/file:\/\/\//g, "file:/");
  2686. };
  2687.  
  2688. this.parseURLParams = function(url)
  2689. {
  2690.     var q = url ? url.indexOf("?") : -1;
  2691.     if (q == -1)
  2692.         return [];
  2693.  
  2694.     var search = url.substr(q+1);
  2695.     var h = search.lastIndexOf("#");
  2696.     if (h != -1)
  2697.         search = search.substr(0, h);
  2698.  
  2699.     if (!search)
  2700.         return [];
  2701.  
  2702.     return this.parseURLEncodedText(search);
  2703. };
  2704.  
  2705. this.parseURLEncodedText = function(text)
  2706. {
  2707.     const maxValueLength = 25000;
  2708.  
  2709.     var params = [];
  2710.  
  2711.     // Unescape '+' characters that are used to encode a space.
  2712.     // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
  2713.     text = text.replace(/\+/g, " ");
  2714.  
  2715.     var args = text.split("&");
  2716.     for (var i = 0; i < args.length; ++i)
  2717.     {
  2718.         try {
  2719.             var parts = args[i].split("=");
  2720.             if (parts.length == 2)
  2721.             {
  2722.                 if (parts[1].length > maxValueLength)
  2723.                     parts[1] = this.$STR("LargeData");
  2724.  
  2725.                 params.push({name: decodeURIComponent(parts[0]), value: decodeURIComponent(parts[1])});
  2726.             }
  2727.             else
  2728.                 params.push({name: decodeURIComponent(parts[0]), value: ""});
  2729.         } 
  2730.         catch (e) 
  2731.         {
  2732.         }
  2733.     }
  2734.  
  2735.     params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
  2736.  
  2737.     return params;
  2738. };
  2739.  
  2740. this.reEncodeURL= function(file, text)
  2741. {
  2742.     var lines = text.split("\n");
  2743.     var params = this.parseURLEncodedText(lines[lines.length-1]);
  2744.  
  2745.     var args = [];
  2746.     for (var i = 0; i < params.length; ++i)
  2747.         args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value));
  2748.  
  2749.     var url = file.href;
  2750.     url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
  2751.     
  2752.     return url;
  2753. };
  2754.  
  2755. this.getResource = function(aURL)
  2756. {
  2757.     var ioService=Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  2758.     try 
  2759.     {
  2760.         var channel=ioService.newChannel(aURL,null,null);
  2761.         var input=channel.open();
  2762.         return FBL.readFromStream(input);
  2763.     }
  2764.     catch (e)
  2765.     {
  2766.     }
  2767. };
  2768.  
  2769. // ************************************************************************************************
  2770. // Network
  2771.  
  2772. this.readFromStream = function(stream, charset, noClose)
  2773. {
  2774.     try
  2775.     {
  2776.         var sis = this.CCSV("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
  2777.         sis.setInputStream(stream);
  2778.  
  2779.         var segments = [];
  2780.         for (var count = stream.available(); count; count = stream.available())
  2781.             segments.push(sis.readBytes(count));
  2782.  
  2783.         if (!noClose)
  2784.             sis.close();
  2785.  
  2786.         var text = segments.join("");
  2787.  
  2788.         try
  2789.         {
  2790.             return this.convertToUnicode(text, charset);
  2791.         }
  2792.         catch (err)
  2793.         {
  2794.         }
  2795.  
  2796.         return text;
  2797.      }
  2798.      catch(exc)
  2799.      {
  2800.      }
  2801. };
  2802.  
  2803. this.readPostTextFromPage = function(url, context)
  2804. {
  2805.     if (url == context.browser.contentWindow.location.href)
  2806.     {
  2807.         try
  2808.         {
  2809.             var webNav = context.browser.webNavigation;
  2810.             var descriptor = this.QI(webNav, Ci.nsIWebPageDescriptor).currentDescriptor;
  2811.             var entry = this.QI(descriptor, Ci.nsISHEntry);
  2812.             if (entry && entry.postData)
  2813.             {
  2814.                 var postStream = this.QI(entry.postData, Ci.nsISeekableStream);
  2815.                 postStream.seek(NS_SEEK_SET, 0);
  2816.  
  2817.                 var charset = context.window.document.characterSet;
  2818.                 return this.readFromStream(postStream, charset, true);
  2819.             }
  2820.          }
  2821.          catch (exc)
  2822.          {
  2823.          }
  2824.      }
  2825. };
  2826.  
  2827. this.readPostTextFromRequest = function(request, context)
  2828. {
  2829.     try
  2830.     {
  2831.         var is = this.QI(request, Ci.nsIUploadChannel).uploadStream;
  2832.         if (is)
  2833.         {
  2834.             var ss = this.QI(is, Ci.nsISeekableStream);
  2835.             if (ss) ss.seek(NS_SEEK_SET, 0);
  2836.             var charset = context.window.document.characterSet;
  2837.             var text = this.readFromStream(is, charset, true);
  2838.             if (ss) ss.seek(NS_SEEK_SET, 0);
  2839.             return text;
  2840.         }
  2841.     }
  2842.     catch(exc)
  2843.     {
  2844.     }
  2845.  
  2846.     return null;
  2847. };
  2848.  
  2849. this.getInputStreamFromString = function(dataString)
  2850. {
  2851.     var stringStream = this.CCIN("@mozilla.org/io/string-input-stream;1", "nsIStringInputStream");
  2852.  
  2853.     if ("data" in stringStream) // Gecko 1.9 or newer
  2854.         stringStream.data = dataString;
  2855.     else // 1.8 or older
  2856.         stringStream.setData(dataString, dataString.length);
  2857.  
  2858.     return stringStream;
  2859. };
  2860.  
  2861. this.getWindowForRequest = function(request) 
  2862. {
  2863.     var webProgress = this.getRequestWebProgress(request);
  2864.     try {
  2865.         if (webProgress)
  2866.             return webProgress.DOMWindow;
  2867.     }
  2868.     catch (ex) {
  2869.     }
  2870.  
  2871.     return null;
  2872. };
  2873.  
  2874. this.getRequestWebProgress = function(request) 
  2875. {
  2876.     try
  2877.     {
  2878.         if (request.notificationCallbacks)
  2879.             return request.notificationCallbacks.getInterface(Ci.nsIWebProgress);
  2880.     } catch (exc) {}
  2881.  
  2882.     try
  2883.     {
  2884.         if (request.loadGroup && request.loadGroup.groupObserver)
  2885.             return request.loadGroup.groupObserver.QueryInterface(Ci.nsIWebProgress);
  2886.     } catch (exc) {}
  2887.  
  2888.     return null;
  2889. };
  2890.  
  2891. // ************************************************************************************************
  2892. // Network Tracing
  2893.  
  2894. this.getStateDescription = function(flag)
  2895. {
  2896.     var state = [];
  2897.     var nsIWebProgressListener = Ci.nsIWebProgressListener;
  2898.     if (flag & nsIWebProgressListener.STATE_START) state.push("STATE_START");
  2899.     else if (flag & nsIWebProgressListener.STATE_REDIRECTING) state.push("STATE_REDIRECTING");
  2900.     else if (flag & nsIWebProgressListener.STATE_TRANSFERRING) state.push("STATE_TRANSFERRING");
  2901.     else if (flag & nsIWebProgressListener.STATE_NEGOTIATING) state.push("STATE_NEGOTIATING");
  2902.     else if (flag & nsIWebProgressListener.STATE_STOP) state.push("STATE_STOP");
  2903.  
  2904.     if (flag & nsIWebProgressListener.STATE_IS_REQUEST) state.push("STATE_IS_REQUEST");
  2905.     if (flag & nsIWebProgressListener.STATE_IS_DOCUMENT) state.push("STATE_IS_DOCUMENT");
  2906.     if (flag & nsIWebProgressListener.STATE_IS_NETWORK) state.push("STATE_IS_NETWORK");
  2907.     if (flag & nsIWebProgressListener.STATE_IS_WINDOW) state.push("STATE_IS_WINDOW");
  2908.     if (flag & nsIWebProgressListener.STATE_RESTORING) state.push("STATE_RESTORING");
  2909.     if (flag & nsIWebProgressListener.STATE_IS_INSECURE) state.push("STATE_IS_INSECURE");
  2910.     if (flag & nsIWebProgressListener.STATE_IS_BROKEN) state.push("STATE_IS_BROKEN");
  2911.     if (flag & nsIWebProgressListener.STATE_IS_SECURE) state.push("STATE_IS_SECURE");
  2912.     if (flag & nsIWebProgressListener.STATE_SECURE_HIGH) state.push("STATE_SECURE_HIGH");
  2913.     if (flag & nsIWebProgressListener.STATE_SECURE_MED) state.push("STATE_SECURE_MED");
  2914.     if (flag & nsIWebProgressListener.STATE_SECURE_LOW) state.push("STATE_SECURE_LOW");
  2915.  
  2916.     return state.join(", ");
  2917. };
  2918.  
  2919. this.getStatusDescription = function(status)
  2920. {
  2921.     var nsISocketTransport = Ci.nsISocketTransport;
  2922.     var nsITransport = Ci.nsITransport;
  2923.  
  2924.     if (status == nsISocketTransport.STATUS_RESOLVING) return "STATUS_RESOLVING";
  2925.     if (status == nsISocketTransport.STATUS_CONNECTING_TO) return "STATUS_CONNECTING_TO";
  2926.     if (status == nsISocketTransport.STATUS_CONNECTED_TO) return "STATUS_CONNECTED_TO";
  2927.     if (status == nsISocketTransport.STATUS_SENDING_TO) return "STATUS_SENDING_TO";
  2928.     if (status == nsISocketTransport.STATUS_WAITING_FOR) return "STATUS_WAITING_FOR";
  2929.     if (status == nsISocketTransport.STATUS_RECEIVING_FROM) return "STATUS_RECEIVING_FROM";
  2930.     if (status == nsITransport.STATUS_READING) return "STATUS_READING";
  2931.     if (status == nsITransport.STATUS_WRITING) return "STATUS_WRITING";
  2932. };
  2933.  
  2934. this.getLoadFlagsDescription = function(loadFlags)
  2935. {
  2936.     var flags = [];
  2937.     var nsIChannel = Ci.nsIChannel;
  2938.     var nsICachingChannel = Ci.nsICachingChannel;
  2939.  
  2940.     if (loadFlags & nsIChannel.LOAD_DOCUMENT_URI) flags.push("LOAD_DOCUMENT_URI");
  2941.     if (loadFlags & nsIChannel.LOAD_RETARGETED_DOCUMENT_URI) flags.push("LOAD_RETARGETED_DOCUMENT_URI");
  2942.     if (loadFlags & nsIChannel.LOAD_REPLACE) flags.push("LOAD_REPLACE");
  2943.     if (loadFlags & nsIChannel.LOAD_INITIAL_DOCUMENT_URI) flags.push("LOAD_INITIAL_DOCUMENT_URI");
  2944.     if (loadFlags & nsIChannel.LOAD_TARGETED) flags.push("LOAD_TARGETED");
  2945.     if (loadFlags & nsIChannel.LOAD_CALL_CONTENT_SNIFFERS) flags.push("LOAD_CALL_CONTENT_SNIFFERS");
  2946.     if (loadFlags & nsICachingChannel.LOAD_NO_NETWORK_IO) flags.push("LOAD_NO_NETWORK_IO");
  2947.     if (loadFlags & nsICachingChannel.LOAD_CHECK_OFFLINE_CACHE) flags.push("LOAD_CHECK_OFFLINE_CACHE");
  2948.     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE) flags.push("LOAD_BYPASS_LOCAL_CACHE");
  2949.     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) flags.push("LOAD_BYPASS_LOCAL_CACHE_IF_BUSY");
  2950.     if (loadFlags & nsICachingChannel.LOAD_ONLY_FROM_CACHE) flags.push("LOAD_ONLY_FROM_CACHE");
  2951.     if (loadFlags & nsICachingChannel.LOAD_ONLY_IF_MODIFIED) flags.push("LOAD_ONLY_IF_MODIFIED");
  2952.  
  2953.     return flags.join(", ");
  2954. };
  2955.  
  2956. // ************************************************************************************************
  2957. // Programs
  2958.  
  2959. this.launchProgram = function(exePath, args)
  2960. {
  2961.     try {
  2962.         var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  2963.         file.initWithPath(exePath);
  2964.         if (this.getPlatformName() == "Darwin" && file.isDirectory())
  2965.         {
  2966.             args = this.extendArray(["-a", exePath], args);
  2967.             file.initWithPath("/usr/bin/open");
  2968.         }
  2969.         if (!file.exists())
  2970.             return false;
  2971.         var process = this.CCIN("@mozilla.org/process/util;1", "nsIProcess");
  2972.         process.init(file);
  2973.         process.run(false, args, args.length, {});
  2974.         return true;
  2975.     }
  2976.     catch(exc)
  2977.     {
  2978.         this.ERROR(exc);
  2979.     }
  2980.     return false;
  2981. };
  2982.  
  2983. this.getIconURLForFile = function(path)
  2984. {
  2985.     var ioService = this.CCSV("@mozilla.org/network/io-service;1", "nsIIOService");
  2986.     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  2987.     try {
  2988.         var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  2989.         file.initWithPath(path);
  2990.         if ((this.getPlatformName() == "Darwin") && !file.isDirectory() && (path.indexOf(".app/") != -1))
  2991.         {
  2992.             path = path.substr(0,path.lastIndexOf(".app/")+4);
  2993.             file.initWithPath(path);
  2994.         }
  2995.         return "moz-icon://" + fileHandler.getURLSpecFromFile(file) + "?size=16";
  2996.     }
  2997.     catch(exc)
  2998.     {
  2999.         this.ERROR(exc);
  3000.     }
  3001.     return null;
  3002. }
  3003.  
  3004. // ************************************************************************************************
  3005.  
  3006. this.getSourceLineRange = function(sourceBox, min, max)
  3007. {
  3008.     var html = [];
  3009.  
  3010.     for (var i = min; i <= max; ++i)
  3011.     {
  3012.         // Make sure all line numbers are the same width (with a fixed-width font)
  3013.         var lineNo = i + "";
  3014.         var maxLineNoChars = sourceBox.maxLineNoChars;
  3015.         while (lineNo.length < maxLineNoChars)
  3016.             lineNo = " " + lineNo;
  3017.  
  3018.         var line =  sourceBox.getLineAsHTML(i-1);
  3019.  
  3020.         html.push(
  3021.             '<div class="sourceRow"><a class="sourceLine">',
  3022.             lineNo,
  3023.             '</a><span class="sourceRowText">',
  3024.             line,
  3025.             '</span></div>'
  3026.         );
  3027.     }
  3028.  
  3029.     return html.join("");
  3030. };
  3031.  
  3032. // ************************************************************************************************
  3033.  
  3034. this.persistObjects = function(panel, panelState)
  3035. {
  3036.     // Persist the location and selection so we can restore them in case of a reload
  3037.     if (panel.location)
  3038.         panelState.persistedLocation = this.persistObject(panel.location, panel.context);
  3039.  
  3040.     if (panel.selection)
  3041.         panelState.persistedSelection = this.persistObject(panel.selection, panel.context);
  3042. };
  3043.  
  3044. this.persistObject = function(object, context)
  3045. {
  3046.     var rep = Firebug.getRep(object);
  3047.     return rep ? rep.persistObject(object, context) : null;
  3048. };
  3049.  
  3050. this.restoreObjects = function(panel, panelState, letMeRetryLater)
  3051. {
  3052.     var needRetry = false;
  3053.     
  3054.     // Persist the location and selection so we can restore them in case of a reload
  3055.     if (!panel.location && panelState && panelState.persistedLocation)
  3056.     {
  3057.         var location = panelState.persistedLocation(panel.context);
  3058.         if (location)
  3059.             panel.navigate(location);
  3060.         else
  3061.             needRetry = true;
  3062.     }
  3063.  
  3064.     if (!panel.location)
  3065.         panel.navigate(null);
  3066.  
  3067.     if (!panel.selection && panelState && panelState.persistedSelection)
  3068.     {
  3069.         var selection = panelState.persistedSelection(panel.context);
  3070.         if (selection)
  3071.             panel.select(selection);
  3072.         else
  3073.             needRetry = true;
  3074.     }
  3075.  
  3076.     if (!panel.selection)  // Couldn't restore the selection, so select the default object
  3077.         panel.select(null);
  3078.     
  3079.     if (needRetry)
  3080.     {
  3081.         function restoreRetry()
  3082.         {
  3083.             if (panel.selection == panel.getDefaultSelection(panel.context) && panelState.persistedSelection)
  3084.             {
  3085.                 var selection = panelState.persistedSelection(panel.context);
  3086.                 if (selection)
  3087.                     panel.select(selection);
  3088.             }
  3089.             if (panel.location == panel.getDefaultLocation(panel.context) && panelState.persistedLocation)
  3090.             {
  3091.                 var location = panelState.persistedLocation(panel.context);
  3092.                 if (location)
  3093.                     panel.navigate(location);
  3094.             }
  3095.         
  3096.         }
  3097.         
  3098.         if (letMeRetryLater)
  3099.             return restoreRetry();
  3100.         else // If we couldn't restore the selection, wait a bit and try again
  3101.             panel.context.setTimeout(restoreRetry, restoreRetryTimeout);
  3102.     }
  3103. };
  3104.  
  3105. this.getPersistedState = function(context, panelName)
  3106. {
  3107.     if (!context)
  3108.         return null;
  3109.  
  3110.     var persistedState = context.persistedState;
  3111.     if (!persistedState)
  3112.         persistedState = context.persistedState = {};
  3113.  
  3114.     if (!persistedState.panelState)
  3115.         persistedState.panelState = {};
  3116.  
  3117.     var panelState = persistedState.panelState[panelName];
  3118.     if (!panelState)
  3119.         panelState = persistedState.panelState[panelName] = {};
  3120.  
  3121.     return panelState;
  3122. };
  3123.  
  3124. // ************************************************************************************************
  3125.  
  3126. this.ErrorMessage = function(message, href, lineNo, source, category, context, trace)
  3127. {
  3128.     this.message = message;
  3129.     this.href = href;
  3130.     this.lineNo = lineNo;
  3131.     this.source = source;
  3132.     this.category = category;
  3133.     this.context = context;
  3134.     this.trace = trace;
  3135. };
  3136.  
  3137. this.ErrorMessage.prototype =
  3138. {
  3139.     getSourceLine: function()
  3140.     {
  3141.         return this.context.sourceCache.getLine(this.href, this.lineNo);
  3142.     }
  3143. };
  3144.  
  3145. // ************************************************************************************************
  3146.  
  3147. this.TextSearch = function(rootNode, rowFinder)
  3148. {
  3149.     var doc = rootNode.ownerDocument;
  3150.     var count, searchRange, startPt, endPt;
  3151.  
  3152.     this.find = function(text)
  3153.     {
  3154.         this.text = text;
  3155.  
  3156.         var range = this.range = finder.Find(text, searchRange, startPt, endPt);
  3157.         var match = range ?  range.startContainer : null;
  3158.         return this.currentNode = (rowFinder && match ? rowFinder(match) : match);
  3159.     };
  3160.  
  3161.     this.findNext = function(wrapAround, sameNode)
  3162.     {
  3163.         startPt = doc.createRange();
  3164.         try
  3165.         {
  3166.             startPt.setStartAfter(this.currentNode ? this.currentNode : rootNode);
  3167.         }
  3168.         catch (e)
  3169.         {
  3170.             try {
  3171.                 FBTrace.sysout("setStart try\n");
  3172.                 startPt.setStart(this.currentNode ? this.currentNode : rootNode);
  3173.                 FBTrace.sysout("setStart success\n");
  3174.             } catch (exc) {
  3175.                 return;
  3176.             }
  3177.         }
  3178.  
  3179.         var match = this.find(this.text);
  3180.         if (!match && wrapAround)
  3181.         {
  3182.             this.reset();
  3183.             return this.find(this.text);
  3184.         }
  3185.  
  3186.         return match;
  3187.     };
  3188.  
  3189.     this.reset = function()
  3190.     {
  3191.         count = rootNode.childNodes.length;
  3192.         searchRange = doc.createRange();
  3193.         searchRange.setStart(rootNode, 0);
  3194.         searchRange.setEnd(rootNode, count);
  3195.  
  3196.         startPt = doc.createRange();
  3197.         startPt.setStart(rootNode, 0);
  3198.         startPt.setEnd(rootNode, 0);
  3199.  
  3200.         endPt = doc.createRange();
  3201.         endPt.setStart(rootNode, count);
  3202.         endPt.setEnd(rootNode, count);
  3203.     };
  3204.  
  3205.     this.reset();
  3206. };
  3207.  
  3208. // ************************************************************************************************
  3209. this.SourceBoxTextSearch = function(sourceBox)
  3210. {
  3211.     this.find = function(text)
  3212.     {
  3213.         this.text = text;
  3214.  
  3215.         this.re = new RegExp(text, 'g');
  3216.         return this.findNext(false);
  3217.     };
  3218.  
  3219.     this.findNext = function(wrapAround)
  3220.     {
  3221.         var match = null;
  3222.         for (var point = this.mark; point < sourceBox.lines.length; point++)
  3223.         {
  3224.             match = this.re.exec(sourceBox.lines[point]);
  3225.             if (match)
  3226.             {
  3227.                 this.mark = point;
  3228.                 return point;
  3229.             }
  3230.         }
  3231.  
  3232.         if (!match && wrapAround)
  3233.         {
  3234.             this.reset();
  3235.             return this.findNext(false);
  3236.         }
  3237.  
  3238.         return match;
  3239.     };
  3240.  
  3241.     this.reset = function()
  3242.     {
  3243.         this.mark = 1;
  3244.     };
  3245.  
  3246.     this.reset();
  3247. };
  3248. //************************************************************************************************
  3249.  
  3250. this.Continued = function()
  3251. {
  3252.  
  3253. };
  3254.  
  3255. this.Continued.prototype =
  3256. {
  3257.     complete: function()
  3258.     {
  3259.         if (this.callback)
  3260.             this.callback.apply(top, arguments);
  3261.         else
  3262.             this.result = cloneArray(arguments);
  3263.     },
  3264.  
  3265.     wait: function(cb)
  3266.     {
  3267.         if ("result" in this)
  3268.             cb.apply(top, this.result);
  3269.         else
  3270.             this.callback = cb;
  3271.     }
  3272. };
  3273.  
  3274. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  3275.  
  3276. this.SourceLink = function(url, line, type, object)
  3277. {
  3278.     this.href = url;
  3279.     this.line = line;
  3280.     this.type = type;
  3281.     this.object = object;
  3282. };
  3283.  
  3284. this.SourceLink.prototype =
  3285. {
  3286.     toString: function()
  3287.     {
  3288.         return this.href;
  3289.     }
  3290. };
  3291.  
  3292. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  3293. // SourceFile one for every compilation unit.
  3294. // Unique URL for each. (href)
  3295. // Unique outerScript, the statements outside of any function defintion
  3296. // sourceCache keyed by href has source for this compilation unit
  3297. // Stored by href in context.
  3298. // Contains array of jsdIScript for functions (scripts) defined in this unit
  3299. // May contain line table (for sources viewed)
  3300. //
  3301. this.SourceFile = function (compilation_unit_type)
  3302. {
  3303.     this.compilation_unit_type = compilation_unit_type; /*@explore*/
  3304.     this.OuterScriptAnalyzer = function(sourceFile)
  3305.     {
  3306.         this.sourceFile = sourceFile;
  3307.     };
  3308. }
  3309.  
  3310. this.SourceFile.prototype =
  3311. {
  3312.     toString: function()
  3313.     {
  3314.         var str = (this.compilation_unit_type?this.compilation_unit_type+" ":"")+this.href+" script.tags( ";
  3315.         if (this.outerScript)
  3316.             str += (this.outerScript.isValid?this.outerScript.tag:"X") +"| ";
  3317.         if (this.innerScripts)
  3318.         {
  3319.             var numberInvalid = 0;
  3320.             for (var p in this.innerScripts)
  3321.             {
  3322.                 var script = this.innerScripts[p];
  3323.                 if (script.isValid)  
  3324.                     str += p+" ";
  3325.                 else
  3326.                     numberInvalid++;
  3327.             }
  3328.         }
  3329.         str += ")"+(numberInvalid ? "("+numberInvalid+" invalid)" : "");
  3330.         return str;
  3331.     },
  3332.  
  3333.     forEachScript: function(callback)
  3334.     {
  3335.         if (this.outerScript)
  3336.             callback(this.outerScript);
  3337.         if (this.innerScripts)
  3338.         {
  3339.             for (var p in this.innerScripts)
  3340.             {
  3341.                 var script = this.innerScripts[p];
  3342.                 var rc = callback(script);
  3343.                 if (rc)
  3344.                     return rc;
  3345.             }
  3346.         }
  3347.     },
  3348.  
  3349.     getSourceLength: function()
  3350.     {
  3351.             return this.sourceLength;
  3352.     },
  3353.  
  3354.     getLine: function(context, lineNo)
  3355.     {
  3356.         return context.sourceCache.getLine(this.href, lineNo);
  3357.     },
  3358.  
  3359.     addToLineTable: function(script)
  3360.     {
  3361.         if (!script || !script.isValid)
  3362.         {
  3363.             return;
  3364.         }
  3365.  
  3366.         // For outer scripts, a better algorithm would loop over PC, use pcToLine to mark the lines.
  3367.         // This assumes there are fewer PCs in an outer script than lines, probably true for large systems.
  3368.         // And now addToLineTable is only used for outerScripts (eval and top-level).
  3369.         // But since we can't know the range of PC values we cannot use that approach.
  3370.  
  3371.         if (!this.outerScriptLineMap)
  3372.             this.outerScriptLineMap = [];
  3373.  
  3374.         var lineCount = script.lineExtent + 1;
  3375.         var offset = this.getBaseLineOffset();
  3376.         if (lineCount > 100)
  3377.             lineCount = 100; // isLineExecutable requires about 1ms per line, so it can only be called for toy programs
  3378.  
  3379.         for (var i = 0; i <= lineCount; i++)
  3380.         {
  3381.             var scriptLineNo = i + script.baseLineNumber;  // the max is (i + script.baseLineNumber + script.lineExtent)
  3382.             var mapLineNo = scriptLineNo - offset;
  3383.             try
  3384.             {
  3385.                 if (script.isLineExecutable(scriptLineNo, this.pcmap_type))
  3386.                     this.outerScriptLineMap.push(mapLineNo);
  3387.             }
  3388.             catch (e)
  3389.             {
  3390.                 // I guess not...
  3391.             }
  3392.                                                                                                                        /*@explore*/
  3393.         }
  3394.     },
  3395.  
  3396.     addToLineTableByPCLoop: function(script)
  3397.     {
  3398.         // This code is not called; it crashes FF3pre https://bugzilla.mozilla.org/show_bug.cgi?id=430205
  3399.         if (!this.outerScriptLineMap)
  3400.             this.outerScriptLineMap = {};
  3401.  
  3402.         var lineCount = script.lineExtent;
  3403.         var offset = this.getBaseLineOffset();
  3404.                                                                                            /*@explore*/
  3405.         for (var i = 0; i <= 10*lineCount; i++)
  3406.         {
  3407.             var lineFromPC = script.pcToLine(i, this.pcmap_type);
  3408.             //FBTrace.sysout("lib.SourceFile.addToLineTableByPCLoop pc="+i+" line: "+lineFromPC+"\n");                                                                                         /*@explore*/
  3409.             this.outerScriptLineMap[lineFromPC] = script;
  3410.             if (lineFromPC >= lineCount) break;
  3411.         }
  3412.  
  3413.     },
  3414.  
  3415.     getScriptsAtLineNumber: function(lineNo, mustBeExecutableLine)
  3416.     {
  3417.         var offset = this.getBaseLineOffset();
  3418.  
  3419.         if (!this.innerScripts)
  3420.             return; // eg URLOnly
  3421.  
  3422.         var targetLineNo = lineNo + offset;  // lineNo is user-viewed number, targetLineNo is jsd number
  3423.  
  3424.         var scripts = [];
  3425.         for (var p in this.innerScripts)
  3426.         {
  3427.             var script = this.innerScripts[p];
  3428.             if (mustBeExecutableLine && !script.isValid) continue;
  3429.             this.addScriptAtLineNumber(scripts, script, targetLineNo, mustBeExecutableLine, offset);
  3430.         }
  3431.         if (this.outerScript && !(mustBeExecutableLine && !this.outerScript.isValid) )
  3432.             this.addScriptAtLineNumber(scripts, this.outerScript, targetLineNo, mustBeExecutableLine, offset);
  3433.  
  3434.         else
  3435.         {
  3436.         }
  3437.  
  3438.         return (scripts.length > 0) ? scripts : false;
  3439.     },
  3440.  
  3441.     addScriptAtLineNumber: function(scripts, script, targetLineNo, mustBeExecutableLine, offset)
  3442.     {
  3443.         // script.isValid will be true.
  3444.         if (targetLineNo >= script.baseLineNumber)
  3445.         {
  3446.             if ( (script.baseLineNumber + script.lineExtent) >= targetLineNo)
  3447.             {
  3448.                 if (mustBeExecutableLine)
  3449.                 {
  3450.                     try
  3451.                     {
  3452.                         if (!script.isLineExecutable(targetLineNo, this.pcmap_type) )
  3453.                         {
  3454.                             return;
  3455.                         }
  3456.                     }
  3457.                     catch (e)
  3458.                     {
  3459.                         // Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [jsdIScript.isLineExecutable]
  3460.                         return;
  3461.                     }
  3462.                 }
  3463.                 scripts.push(script);
  3464.             }
  3465.         }
  3466.     },
  3467.  
  3468.     scriptsIfLineCouldBeExecutable: function(lineNo)  // script may not be valid
  3469.     {
  3470.         var scripts = this.getScriptsAtLineNumber(lineNo, true);
  3471.         if (!scripts && this.outerScriptLineMap && (this.outerScriptLineMap.indexOf(lineNo) != -1) )
  3472.             return [this.outerScript];
  3473.         return scripts;
  3474.     },
  3475.  
  3476.     hasScript: function(script)
  3477.     {
  3478.         if (this.outerScript && (this.outerScript.tag == script.tag) )
  3479.             return true;
  3480.         // XXXjjb Don't use indexOf or similar tests that rely on ===, since we are really working with
  3481.         // wrappers around jsdIScript, not script themselves.  I guess.
  3482.  
  3483.        return ( this.innerScripts && this.innerScripts.hasOwnProperty(script.tag) );
  3484.     },
  3485.  
  3486.     // these objects map JSD's values to correct values
  3487.     getScriptAnalyzer: function(script)
  3488.     {
  3489.         if (this.outerScript && (script.tag == this.outerScript.tag) )
  3490.             return new this.OuterScriptAnalyzer(this); // depends on type
  3491.         return new this.NestedScriptAnalyzer(this);
  3492.     },
  3493.  
  3494.     NestedScriptAnalyzer: function(sourceFile)
  3495.     {
  3496.         this.sourceFile = sourceFile;
  3497.     },
  3498.  
  3499.     // return.path: group/category label, return.name: item label
  3500.     getObjectDescription: function()
  3501.     {
  3502.         return FBL.splitURLBase(this.href);
  3503.     },
  3504.  
  3505.     isEval: function()
  3506.     {
  3507.         return (this.compilation_unit_type == "eval-level") || (this.compilation_unit_type == "newFunction");
  3508.     },
  3509.  
  3510.     isEvent: function()
  3511.     {
  3512.         return (this.compilation_unit_type == "event");
  3513.     }
  3514. }
  3515.  
  3516. this.SourceFile.prototype.NestedScriptAnalyzer.prototype =
  3517. {
  3518.     // Adjust JSD line numbers based on origin of script
  3519.     getSourceLineFromFrame: function(context, frame)
  3520.     {
  3521.         return frame.line - (this.sourceFile.getBaseLineOffset());
  3522.     },
  3523.     // Interpret frame to give fn(args)
  3524.     getFunctionDescription: function(script, context, frame)
  3525.     {
  3526.         if (frame)
  3527.         {
  3528.             var name = frame.name;
  3529.             var fnc = script.functionObject.getWrappedValue();
  3530.             var args = FBL.getFunctionArgValues(fnc, frame);
  3531.         }
  3532.         else
  3533.         {
  3534.             var name = script.functionName;
  3535.             var args = [];
  3536.         }
  3537.  
  3538.         if (name ==  "anonymous")
  3539.         {
  3540.             name = FBL.guessFunctionName(this.sourceFile.href, this.getBaseLineNumberByScript(script), context);
  3541.         }
  3542.  
  3543.         return {name: name, args: args};
  3544.     },
  3545.  
  3546.     // link to source for this script.
  3547.     getSourceLinkForScript: function (script)
  3548.     {
  3549.         var line = this.getBaseLineNumberByScript(script);
  3550.         return new FBL.SourceLink(this.sourceFile.href, line, "js");
  3551.     },
  3552.  
  3553.     getBaseLineNumberByScript: function(script)
  3554.     {
  3555.         return script.baseLineNumber - (this.sourceFile.getBaseLineOffset() - 1);
  3556.     }
  3557. }
  3558.  
  3559. this.addScriptsToSourceFile = function(sourceFile, outerScript, innerScripts)
  3560. {
  3561. /*    if (outerScript.isValid)
  3562.     {
  3563.         try
  3564.         {
  3565.             sourceFile.addToLineTable(outerScript, outerScript.baseLineNumber);
  3566.         }
  3567.         catch (exc)
  3568.         {
  3569.             // XXXjjb I think this is happening when we go out of the script range in isLineExecutable.
  3570.         }
  3571.     }
  3572. */
  3573.  
  3574.     // Attach the innerScripts for use later
  3575.     if (!sourceFile.innerScripts)
  3576.         sourceFile.innerScripts = {};
  3577.  
  3578.     var total = 0;
  3579.     while (innerScripts.hasMoreElements())
  3580.     {
  3581.         var script = innerScripts.getNext();
  3582.         if (!script || ( (script instanceof Ci.jsdIScript) && !script.tag) )
  3583.         {
  3584.             continue;            
  3585.         }
  3586.         sourceFile.innerScripts[script.tag] = script;
  3587.         total++
  3588.     }
  3589. }
  3590.  
  3591. //------------
  3592. this.EvalLevelSourceFile = function(url, script, eval_expr, source, innerScriptEnumerator) // ctor
  3593. {
  3594.     this.href = url;
  3595.     this.outerScript = script;
  3596.     this.evalExpression = eval_expr;
  3597.     this.sourceLength = source.length;
  3598.     this.source = source;
  3599.     this.pcmap_type = PCMAP_SOURCETEXT;
  3600.     FBL.addScriptsToSourceFile(this, script, innerScriptEnumerator);
  3601. };
  3602.  
  3603. this.EvalLevelSourceFile.prototype = new this.SourceFile("eval-level"); // shared prototype
  3604.  
  3605. this.EvalLevelSourceFile.prototype.getLine = function(context, lineNo)
  3606. {
  3607.     return this.source[lineNo - 1];
  3608. },
  3609.  
  3610. this.EvalLevelSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3611. {
  3612.     // Adjust JSD line numbers based on origin of script
  3613.     getSourceLineFromFrame: function(context, frame)
  3614.     {
  3615.         return frame.line - this.sourceFile.getBaseLineOffset();
  3616.     },
  3617.     // Interpret frame to give fn(args)
  3618.     getFunctionDescription: function(script, context, frame)
  3619.     {
  3620.         return {name: "eval", args: [this.evalExpression] };
  3621.     },
  3622.     getSourceLinkForScript: function (script)
  3623.     {
  3624.         return new FBL.SourceLink(this.sourceFile.href, 1, "js");
  3625.     }
  3626. }
  3627.  
  3628. this.EvalLevelSourceFile.prototype.getBaseLineOffset = function()
  3629. {
  3630.     return this.outerScript.baseLineNumber - 1; // baseLineNumber always valid even after jsdIscript isValid false
  3631. }
  3632.  
  3633. this.EvalLevelSourceFile.prototype.getObjectDescription = function()
  3634. {
  3635.     if (this.href.kind == "source")
  3636.         return FBL.splitURL(this.href);
  3637.  
  3638.     if (!this.summary)
  3639.     {
  3640.         if (this.evalExpression)
  3641.             this.summary = FBL.summarizeSourceLineArray(this.evalExpression.substr(0, 240), 120);
  3642.         if (!this.summary)
  3643.             this.summary = "";
  3644.         if (this.summary.length < 120)
  3645.             this.summary = "eval("+this.summary + "...)=" + FBL.summarizeSourceLineArray(this.source, 120 - this.summary.length);
  3646.     }
  3647.     return {path: this.href.replace(/\/eval\/[^\/]+$/, "/eval"), name: this.summary };
  3648. }
  3649. //------------
  3650. this.EventSourceFile = function(url, script, title, source, innerScriptEnumerator)
  3651. {
  3652.     this.href = url;
  3653.     this.outerScript = script;
  3654.     this.title = title;
  3655.     this.sourceLines = source; // points to the sourceCache lines
  3656.     this.sourceLength = source.length;
  3657.     this.pcmap_type = PCMAP_PRETTYPRINT;
  3658.  
  3659.     FBL.addScriptsToSourceFile(this, script, innerScriptEnumerator);
  3660. };
  3661.  
  3662. this.EventSourceFile.prototype = new this.SourceFile("event"); // prototypical inheritance
  3663.  
  3664.  
  3665. this.EvalLevelSourceFile.prototype.getLine = function(context, lineNo)
  3666. {
  3667.     return this.sourceLines[lineNo - 1];
  3668. },
  3669.  
  3670.  
  3671. this.EventSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3672. {
  3673.     // Adjust JSD line numbers based on origin of script
  3674.     getSourceLineFromFrame: function(context, frame)
  3675.     {
  3676.         var script = frame.script;
  3677.         var line = script.pcToLine(frame.pc, PCMAP_PRETTYPRINT);
  3678.         return line - 1;
  3679.     },
  3680.     // Interpret frame to give fn(args)
  3681.     getFunctionDescription: function(script, context, frame)
  3682.     {
  3683.         var fn = script.functionObject.getWrappedValue();  //?? should be name of?
  3684.         if (frame)
  3685.             var args = FBL.getFunctionArgValues(fn, frame);
  3686.         else
  3687.             var args = [];
  3688.         return {name: fn, args: args};
  3689.     },
  3690.     getSourceLinkForScript: function (script)
  3691.     {
  3692.         return new FBL.SourceLink(this.sourceFile.href, 1, "js");  // XXXjjb why do we need FBL.??
  3693.     }
  3694. }
  3695.  
  3696. this.EventSourceFile.prototype.getBaseLineOffset = function()
  3697. {
  3698.     return 1;
  3699. }
  3700.  
  3701. this.summarizeSourceLineArray = function(sourceLines, size)
  3702. {
  3703.     var buf  = "";
  3704.     for (var i = 0; i < sourceLines.length; i++)
  3705.     {
  3706.         var aLine = sourceLines[i].substr(0,240);  // avoid huge lines
  3707.         buf += aLine.replace(/\s/, " ", "g");
  3708.         if (buf.length > size || aLine.length > 240)
  3709.             break;
  3710.     }
  3711.     return buf.substr(0, size);
  3712. };
  3713.  
  3714. this.EventSourceFile.prototype.getObjectDescription = function()
  3715. {
  3716.     if (!this.summary)
  3717.     {
  3718.         this.summary = FBL.summarizeSourceLineArray(this.sourceLines, 120);
  3719.     }
  3720.  
  3721.     return {path: this.href.replace(/\/event\/[^\/]+$/, "/event"), name: this.summary };
  3722. }
  3723.  
  3724. //-----------
  3725. this.FunctionConstructorSourceFile = function(url, script, ctor_expr, sourceLength, innerScriptEnumerator)
  3726. {
  3727.     this.href = url;
  3728.     this.outerScript = script;
  3729.     this.evalExpression = eval_expr;
  3730.     this.sourceLength = sourceLength;
  3731.     this.pcmap_type = PCMAP_SOURCETEXT;
  3732.  
  3733.     FBL.addScriptsToSourceFile(this, script, innerScriptEnumerator);
  3734. }
  3735.  
  3736. this.FunctionConstructorSourceFile.prototype = new this.SourceFile("newFunction");
  3737.  
  3738. this.FunctionConstructorSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3739. {
  3740.     // Adjust JSD line numbers based on origin of script
  3741.     getSourceLineFromFrame: function(context, frame)
  3742.     {
  3743.         return frame.line - this.sourceFile.getBaseLineOffset();
  3744.     },
  3745.     // Interpret frame to give fn(args)
  3746.     getFunctionDescription: function(script, context, frame)
  3747.     {
  3748.         return {name: "new Function", args: [this.evalExpression] };
  3749.     },
  3750.     getSourceLinkForScript: function (script)
  3751.     {
  3752.         return new SourceLink(this.sourceFile.href, 1, "js");
  3753.     }
  3754. }
  3755. this.FunctionConstructorSourceFile.prototype.getBaseLineOffset = function()
  3756. {
  3757.     this.outerScript.baseLineNumber; // always valid
  3758. }
  3759.  
  3760. //-----------
  3761. this.TopLevelSourceFile = function(url, outerScript, sourceLength, innerScriptEnumerator)
  3762. {
  3763.     this.href = url;
  3764.     this.outerScript = outerScript;  // Beware may not be valid after we return!!
  3765.     this.sourceLength = sourceLength;
  3766.     this.pcmap_type = PCMAP_SOURCETEXT;
  3767.  
  3768.     FBL.addScriptsToSourceFile(this, outerScript, innerScriptEnumerator);
  3769. }
  3770.  
  3771. this.TopLevelSourceFile.prototype = new this.SourceFile("top-level");
  3772.  
  3773. this.TopLevelSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3774. {
  3775.     // Adjust JSD line numbers based on origin of script
  3776.     getSourceLineFromFrame: function(context, frame)
  3777.     {
  3778.         return frame.line;
  3779.     },
  3780.     // Interpret frame to give fn(args)
  3781.     getFunctionDescription: function(script, context, frame)
  3782.     {
  3783.         var file_name = FBL.getFileName(FBL.normalizeURL(script.fileName)); // this is more useful that just "top_level"
  3784.         file_name = file_name ? file_name: "__top_level__";
  3785.         return {name: file_name, args: []};
  3786.     },
  3787.     getSourceLinkForScript: function (script)
  3788.     {
  3789.         return SourceLink(FBL.normalizeURL(script.fileName), script.baseLineNumber, "js")
  3790.     }
  3791. }
  3792.  
  3793. this.TopLevelSourceFile.prototype.getBaseLineOffset = function()
  3794. {
  3795.     return 0;  // TODO depends on number of script tags https://bugzilla.mozilla.org/show_bug.cgi?id=396568
  3796. }
  3797. //-------
  3798. // A source included more than one time
  3799. this.ReusedSourceFile = function(sourceFile, outerScript, innerScriptEnumerator)
  3800. {
  3801. // need to override most functions.
  3802. }
  3803.  
  3804. //-------
  3805. this.EnumeratedSourceFile = function(context, url) // we don't have the outer script and we delay source load.
  3806. {
  3807.     this.context = context;  // XXXjjb the context of the enumeration, not useful
  3808.     this.href = url;  // may not be outerScript file name, eg this could be an enumerated eval
  3809.     this.innerScripts = {};
  3810.     this.pcmap_type = PCMAP_SOURCETEXT;
  3811. }
  3812.  
  3813. this.EnumeratedSourceFile.prototype = new this.SourceFile("enumerated");
  3814.  
  3815. this.EnumeratedSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3816.     this.TopLevelSourceFile.prototype.OuterScriptAnalyzer.prototype;
  3817.  
  3818. this.EnumeratedSourceFile.prototype.getBaseLineOffset = function()
  3819. {
  3820.     // The outer script is gone, that is what the frame.line is relative to?
  3821.     return 0;  // TODO
  3822. }
  3823.  
  3824. this.EnumeratedSourceFile.prototype.getSourceLength = function()
  3825. {
  3826.     if (!this.sourceLength)
  3827.         this.sourceLength = this.context.sourceCache.load(this.href).length;
  3828.     return this.sourceLength;
  3829. }
  3830. //---------
  3831. this.NoScriptSourceFile = function(context, url) // Somehow we got the URL, but not the script
  3832. {
  3833.      this.href = url;  // we know this much
  3834.      this.innerScripts = {};
  3835. }
  3836.  
  3837. this.NoScriptSourceFile.prototype = new this.SourceFile("URLOnly");
  3838.  
  3839. this.NoScriptSourceFile.prototype.OuterScriptAnalyzer.prototype =
  3840.     this.TopLevelSourceFile.prototype.OuterScriptAnalyzer.prototype;
  3841.  
  3842. this.NoScriptSourceFile.prototype.getBaseLineOffset = function()
  3843. {
  3844.     // The outer script is gone, that is what the frame.line is relative to?
  3845.     return 0;  // TODO
  3846. }
  3847.  
  3848. this.NoScriptSourceFile.prototype.getSourceLength = function()
  3849. {
  3850.     if (!this.sourceLength)
  3851.         this.sourceLength = this.context.sourceCache.load(this.href).length;
  3852.     return this.sourceLength;
  3853. }
  3854. //---------
  3855.  
  3856. this.ScriptTagSourceFile = function(context, url, scriptTagNumber) // we don't have the outer script and we delay source load
  3857. {
  3858.     this.context = context;
  3859.     this.href = url;  // we know this is not an eval
  3860.     this.scriptTagNumber = scriptTagNumber;
  3861.     this.innerScripts = {};
  3862.     this.pcmap_type = PCMAP_SOURCETEXT;
  3863. }
  3864.  
  3865. this.ScriptTagSourceFile.prototype = new this.SourceFile("scriptTag");
  3866.  
  3867. this.ScriptTagSourceFile.prototype.OuterScriptAnalyzer =
  3868.     this.TopLevelSourceFile.prototype.OuterScriptAnalyzer;
  3869.  
  3870. this.ScriptTagSourceFile.prototype.getBaseLineOffset = function()
  3871. {
  3872.     return 0;
  3873. }
  3874.  
  3875. this.ScriptTagSourceFile.prototype.getSourceLength = function()
  3876. {
  3877.     return this.context.sourceCache.load(this.href).length;
  3878. }
  3879.  
  3880. this.ScriptTagSourceFile.prototype.cache = function(context)
  3881. {
  3882.     return context.sourceCache.load(this.href);
  3883. },
  3884.  
  3885. //-------------------
  3886. this.getSourceFileByScript = function(context, script)
  3887. {
  3888.     if (!context.sourceFileMap)
  3889.         return null;
  3890.  
  3891.     // Other algorithms are possible:
  3892.     //   We could store an index, context.sourceFileByTag
  3893.     //   Or we could build a tree keyed by url, with SpiderMonkey script.fileNames at the top and our urls below
  3894.     var lucky = context.sourceFileMap[script.fileName];  // we won't be lucky for file:/ urls, no normalizeURL applied
  3895.     if (lucky && lucky.hasScript(script))
  3896.         return lucky;
  3897.  
  3898.     for (var url in context.sourceFileMap)
  3899.     {
  3900.         var sourceFile = context.sourceFileMap[url];
  3901.         if (sourceFile.hasScript(script))
  3902.             return sourceFile;
  3903.     }
  3904. };
  3905.  
  3906. this.getScriptAnalyzer = function(context, script)
  3907. {
  3908.     var sourceFile = this.getSourceFileByScript(context, script);
  3909.     if (sourceFile)
  3910.     {
  3911.         var analyzer = sourceFile.getScriptAnalyzer(script);
  3912.         return analyzer;
  3913.     }
  3914. };
  3915.  
  3916. this.getSourceFileAndLineByScript= function(context, script, frame)
  3917. {
  3918.     var sourceFile = FBL.getSourceFileByScript(context, script);
  3919.     if (sourceFile)
  3920.     {
  3921.         var analyzer = sourceFile.getScriptAnalyzer(script);
  3922.         if (analyzer)
  3923.             var line = frame ? analyzer.getSourceLineFromFrame(context, frame) : analyzer.getBaseLineNumberByScript(script);
  3924.         else
  3925.             var line = 0;
  3926.  
  3927.         return { sourceFile: sourceFile, lineNo: line };
  3928.     }
  3929. };
  3930.  
  3931. this.guessEnclosingFunctionName = function(url, line)
  3932. {
  3933.     var sourceFile = this.context.sourceFileMap[url];
  3934.     if (sourceFile)
  3935.     {
  3936.         var scripts = sourceFile.getScriptsAtLineNumber(line);
  3937.         if (scripts)
  3938.         {
  3939.             var script = scripts[0]; // TODO try others?
  3940.             var analyzer = sourceFile.getScriptAnalyzer(script);
  3941.             line = analyzer.getBaseLineNumberByScript(script);
  3942.         }
  3943.     }
  3944.      return guessFunctionName(url, line-1, context);
  3945. };
  3946.  
  3947. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  3948.  
  3949. this.SourceText = function(lines, owner)
  3950. {
  3951.     this.lines = lines;
  3952.     this.owner = owner;
  3953. };
  3954.  
  3955. this.SourceText.getLineAsHTML = function(lineNo)
  3956. {
  3957.     return escapeHTML(this.lines[lineNo-1]);
  3958. };
  3959.  
  3960. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  3961.  
  3962. this.StackTrace = function()
  3963. {
  3964.     this.frames = [];
  3965. };
  3966.  
  3967. this.StackTrace.prototype =
  3968. {
  3969.     toString: function()
  3970.     {
  3971.         var trace = "<top>\n";
  3972.         for (var i = 0; i < this.frames.length; i++)
  3973.         {
  3974.             trace += "[" + i + "]"+ this.frames[i]+"\n";
  3975.         }
  3976.         trace += "<bottom>\n";
  3977.         return trace;
  3978.     },
  3979.     reverse: function()
  3980.     {
  3981.         this.frames.reverse();
  3982.         return this;
  3983.     },
  3984.  
  3985.     destroy: function()
  3986.     {
  3987.         for (var i = 0; i < this.frames.length; i++)
  3988.         {
  3989.             this.frames[i].destroy();
  3990.         }
  3991.     }
  3992. };
  3993.  
  3994. this.traceToString = function(trace)                                                                                   /*@explore*/
  3995. {                                                                                                                      /*@explore*/
  3996.     var str = "<top>";                                                                                                 /*@explore*/
  3997.     for(var i = 0; i < trace.frames.length; i++)                                                                       /*@explore*/
  3998.         str += "\n" + trace.frames[i];                                                                                 /*@explore*/
  3999.     str += "\n<bottom>";                                                                                               /*@explore*/
  4000.     return str;                                                                                                        /*@explore*/
  4001. }                                                                                                                      /*@explore*/
  4002. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4003.  
  4004. this.StackFrame = function(context, fn, script, href, lineNo, args, pc)
  4005. {
  4006.     this.context = context;
  4007.     this.fn = fn;
  4008.     this.script = script;
  4009.     this.href = href;
  4010.     this.lineNo = lineNo;
  4011.     this.args = args;
  4012.     this.flags = (script?script.flags:null);
  4013.     this.pc = pc;
  4014. };
  4015.  
  4016. this.StackFrame.prototype =
  4017. {
  4018.     toString: function()
  4019.     {
  4020.         // XXXjjb analyze args and fn?
  4021.         if (this.script)
  4022.             return "("+this.flags+")"+this.href+":"+this.script.baseLineNumber+"-"
  4023.                   +(this.script.baseLineNumber+this.script.lineExtent)+"@"+this.lineNo;
  4024.         else
  4025.             return this.href;
  4026.     },
  4027.     destroy: function()
  4028.     {
  4029.         this.script = null;
  4030.         this.fn = null;
  4031.     },
  4032.     signature: function()
  4033.     {
  4034.         return this.script.tag +"." + this.pc;
  4035.     }
  4036. };
  4037.  
  4038. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4039.  
  4040. this.Property = function(object, name)
  4041. {
  4042.     this.object = object;
  4043.     this.name = name;
  4044.  
  4045.     this.getObject = function()
  4046.     {
  4047.         return object[name];
  4048.     };
  4049. };
  4050.  
  4051. this.ErrorCopy = function(message)
  4052. {
  4053.     this.message = message;
  4054. };
  4055.  
  4056. function EventCopy(event)
  4057. {
  4058.     // Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to
  4059.     // represent them long term in the inspector.
  4060.     for (var name in event)
  4061.     {
  4062.         try {
  4063.             this[name] = event[name];
  4064.         } catch (exc) { }
  4065.     }
  4066. }
  4067.  
  4068. this.EventCopy = EventCopy;
  4069.  
  4070. // ************************************************************************************************
  4071. // DOM Constants
  4072.  
  4073. this.getDOMMembers = function(object)
  4074. {
  4075.     if (!domMemberCache)
  4076.     {
  4077.         domMemberCache = {};
  4078.  
  4079.         for (var name in domMemberMap)
  4080.         {
  4081.             var builtins = domMemberMap[name];
  4082.             var cache = domMemberCache[name] = {};
  4083.  
  4084.             for (var i = 0; i < builtins.length; ++i)
  4085.                 cache[builtins[i]] = i;
  4086.         }
  4087.     }
  4088.  
  4089.     if (object instanceof Window)
  4090.         { return domMemberCache.Window; }
  4091.     else if (object instanceof Document || object instanceof XMLDocument)
  4092.         { return domMemberCache.Document; }
  4093.     else if (object instanceof Location)
  4094.         { return domMemberCache.Location; }
  4095.     else if (object instanceof HTMLImageElement)
  4096.         { return domMemberCache.HTMLImageElement; }
  4097.     else if (object instanceof HTMLAnchorElement)
  4098.         { return domMemberCache.HTMLAnchorElement; }
  4099.     else if (object instanceof HTMLInputElement)
  4100.         { return domMemberCache.HTMLInputElement; }
  4101.     else if (object instanceof HTMLButtonElement)
  4102.         { return domMemberCache.HTMLButtonElement; }
  4103.     else if (object instanceof HTMLFormElement)
  4104.         { return domMemberCache.HTMLFormElement; }
  4105.     else if (object instanceof HTMLBodyElement)
  4106.         { return domMemberCache.HTMLBodyElement; }
  4107.     else if (object instanceof HTMLHtmlElement)
  4108.         { return domMemberCache.HTMLHtmlElement; }
  4109.     else if (object instanceof HTMLScriptElement)
  4110.         { return domMemberCache.HTMLScriptElement; }
  4111.     else if (object instanceof HTMLTableElement)
  4112.         { return domMemberCache.HTMLTableElement; }
  4113.     else if (object instanceof HTMLTableRowElement)
  4114.         { return domMemberCache.HTMLTableRowElement; }
  4115.     else if (object instanceof HTMLTableCellElement)
  4116.         { return domMemberCache.HTMLTableCellElement; }
  4117.     else if (object instanceof HTMLIFrameElement)
  4118.         { return domMemberCache.HTMLIFrameElement; }
  4119.     else if (object instanceof SVGSVGElement)
  4120.         { return domMemberCache.SVGSVGElement; }
  4121.     else if (object instanceof SVGElement)
  4122.         { return domMemberCache.SVGElement; }
  4123.     else if (object instanceof Element)
  4124.         { return domMemberCache.Element; }
  4125.     else if (object instanceof Text || object instanceof CDATASection)
  4126.         { return domMemberCache.Text; }
  4127.     else if (object instanceof Attr)
  4128.         { return domMemberCache.Attr; }
  4129.     else if (object instanceof Node)
  4130.         { return domMemberCache.Node; }
  4131.     else if (object instanceof Event || object instanceof EventCopy)
  4132.         { return domMemberCache.Event; }
  4133.     else
  4134.         return {};
  4135. };
  4136.  
  4137. this.isDOMMember = function(object, propName)
  4138. {
  4139.     var members = this.getDOMMembers(object);
  4140.     return members && propName in members;
  4141. };
  4142.  
  4143. var domMemberCache = null;
  4144. var domMemberMap = {};
  4145.  
  4146. domMemberMap.Window =
  4147. [
  4148.     "document",
  4149.     "frameElement",
  4150.  
  4151.     "innerWidth",
  4152.     "innerHeight",
  4153.     "outerWidth",
  4154.     "outerHeight",
  4155.     "screenX",
  4156.     "screenY",
  4157.     "pageXOffset",
  4158.     "pageYOffset",
  4159.     "scrollX",
  4160.     "scrollY",
  4161.     "scrollMaxX",
  4162.     "scrollMaxY",
  4163.  
  4164.     "status",
  4165.     "defaultStatus",
  4166.  
  4167.     "parent",
  4168.     "opener",
  4169.     "top",
  4170.     "window",
  4171.     "content",
  4172.     "self",
  4173.  
  4174.     "location",
  4175.     "history",
  4176.     "frames",
  4177.     "navigator",
  4178.     "screen",
  4179.     "menubar",
  4180.     "toolbar",
  4181.     "locationbar",
  4182.     "personalbar",
  4183.     "statusbar",
  4184.     "directories",
  4185.     "scrollbars",
  4186.     "fullScreen",
  4187.     "netscape",
  4188.     "java",
  4189.     "console",
  4190.     "Components",
  4191.     "controllers",
  4192.     "closed",
  4193.     "crypto",
  4194.     "pkcs11",
  4195.  
  4196.     "name",
  4197.     "property",
  4198.     "length",
  4199.  
  4200.     "sessionStorage",
  4201.     "globalStorage",
  4202.  
  4203.     "setTimeout",
  4204.     "setInterval",
  4205.     "clearTimeout",
  4206.     "clearInterval",
  4207.     "addEventListener",
  4208.     "removeEventListener",
  4209.     "dispatchEvent",
  4210.     "getComputedStyle",
  4211.     "captureEvents",
  4212.     "releaseEvents",
  4213.     "routeEvent",
  4214.     "enableExternalCapture",
  4215.     "disableExternalCapture",
  4216.     "moveTo",
  4217.     "moveBy",
  4218.     "resizeTo",
  4219.     "resizeBy",
  4220.     "scroll",
  4221.     "scrollTo",
  4222.     "scrollBy",
  4223.     "scrollByLines",
  4224.     "scrollByPages",
  4225.     "sizeToContent",
  4226.     "setResizable",
  4227.     "getSelection",
  4228.     "open",
  4229.     "openDialog",
  4230.     "close",
  4231.     "alert",
  4232.     "confirm",
  4233.     "prompt",
  4234.     "dump",
  4235.     "focus",
  4236.     "blur",
  4237.     "find",
  4238.     "back",
  4239.     "forward",
  4240.     "home",
  4241.     "stop",
  4242.     "print",
  4243.     "atob",
  4244.     "btoa",
  4245.     "updateCommands",
  4246.     "XPCNativeWrapper",
  4247.     "GeckoActiveXObject",
  4248.     "applicationCache"      // FF3
  4249. ];
  4250.  
  4251. domMemberMap.Location =
  4252. [
  4253.     "href",
  4254.     "protocol",
  4255.     "host",
  4256.     "hostname",
  4257.     "port",
  4258.     "pathname",
  4259.     "search",
  4260.     "hash",
  4261.  
  4262.     "assign",
  4263.     "reload",
  4264.     "replace"
  4265. ];
  4266.  
  4267. domMemberMap.Node =
  4268. [
  4269.     "id",
  4270.     "className",
  4271.  
  4272.     "nodeType",
  4273.     "tagName",
  4274.     "nodeName",
  4275.     "localName",
  4276.     "prefix",
  4277.     "namespaceURI",
  4278.     "nodeValue",
  4279.  
  4280.     "ownerDocument",
  4281.     "parentNode",
  4282.     "offsetParent",
  4283.     "nextSibling",
  4284.     "previousSibling",
  4285.     "firstChild",
  4286.     "lastChild",
  4287.     "childNodes",
  4288.     "attributes",
  4289.  
  4290.     "dir",
  4291.     "baseURI",
  4292.     "textContent",
  4293.     "innerHTML",
  4294.  
  4295.     "addEventListener",
  4296.     "removeEventListener",
  4297.     "dispatchEvent",
  4298.     "cloneNode",
  4299.     "appendChild",
  4300.     "insertBefore",
  4301.     "replaceChild",
  4302.     "removeChild",
  4303.     "compareDocumentPosition",
  4304.     "hasAttributes",
  4305.     "hasChildNodes",
  4306.     "lookupNamespaceURI",
  4307.     "lookupPrefix",
  4308.     "normalize",
  4309.     "isDefaultNamespace",
  4310.     "isEqualNode",
  4311.     "isSameNode",
  4312.     "isSupported",
  4313.     "getFeature",
  4314.     "getUserData",
  4315.     "setUserData"
  4316. ];
  4317.  
  4318. domMemberMap.Document = extendArray(domMemberMap.Node,
  4319. [
  4320.     "documentElement",
  4321.     "body",
  4322.     "title",
  4323.     "location",
  4324.     "referrer",
  4325.     "cookie",
  4326.     "contentType",
  4327.     "lastModified",
  4328.     "characterSet",
  4329.     "inputEncoding",
  4330.     "xmlEncoding",
  4331.     "xmlStandalone",
  4332.     "xmlVersion",
  4333.     "strictErrorChecking",
  4334.     "documentURI",
  4335.     "URL",
  4336.  
  4337.     "defaultView",
  4338.     "doctype",
  4339.     "implementation",
  4340.     "styleSheets",
  4341.     "images",
  4342.     "links",
  4343.     "forms",
  4344.     "anchors",
  4345.     "embeds",
  4346.     "plugins",
  4347.     "applets",
  4348.  
  4349.     "width",
  4350.     "height",
  4351.  
  4352.     "designMode",
  4353.     "compatMode",
  4354.     "async",
  4355.     "preferredStylesheetSet",
  4356.  
  4357.     "alinkColor",
  4358.     "linkColor",
  4359.     "vlinkColor",
  4360.     "bgColor",
  4361.     "fgColor",
  4362.     "domain",
  4363.  
  4364.     "addEventListener",
  4365.     "removeEventListener",
  4366.     "dispatchEvent",
  4367.     "captureEvents",
  4368.     "releaseEvents",
  4369.     "routeEvent",
  4370.     "clear",
  4371.     "open",
  4372.     "close",
  4373.     "execCommand",
  4374.     "execCommandShowHelp",
  4375.     "getElementsByName",
  4376.     "getSelection",
  4377.     "queryCommandEnabled",
  4378.     "queryCommandIndeterm",
  4379.     "queryCommandState",
  4380.     "queryCommandSupported",
  4381.     "queryCommandText",
  4382.     "queryCommandValue",
  4383.     "write",
  4384.     "writeln",
  4385.     "adoptNode",
  4386.     "appendChild",
  4387.     "removeChild",
  4388.     "renameNode",
  4389.     "cloneNode",
  4390.     "compareDocumentPosition",
  4391.     "createAttribute",
  4392.     "createAttributeNS",
  4393.     "createCDATASection",
  4394.     "createComment",
  4395.     "createDocumentFragment",
  4396.     "createElement",
  4397.     "createElementNS",
  4398.     "createEntityReference",
  4399.     "createEvent",
  4400.     "createExpression",
  4401.     "createNSResolver",
  4402.     "createNodeIterator",
  4403.     "createProcessingInstruction",
  4404.     "createRange",
  4405.     "createTextNode",
  4406.     "createTreeWalker",
  4407.     "domConfig",
  4408.     "evaluate",
  4409.     "evaluateFIXptr",
  4410.     "evaluateXPointer",
  4411.     "getAnonymousElementByAttribute",
  4412.     "getAnonymousNodes",
  4413.     "addBinding",
  4414.     "removeBinding",
  4415.     "getBindingParent",
  4416.     "getBoxObjectFor",
  4417.     "setBoxObjectFor",
  4418.     "getElementById",
  4419.     "getElementsByTagName",
  4420.     "getElementsByTagNameNS",
  4421.     "hasAttributes",
  4422.     "hasChildNodes",
  4423.     "importNode",
  4424.     "insertBefore",
  4425.     "isDefaultNamespace",
  4426.     "isEqualNode",
  4427.     "isSameNode",
  4428.     "isSupported",
  4429.     "load",
  4430.     "loadBindingDocument",
  4431.     "lookupNamespaceURI",
  4432.     "lookupPrefix",
  4433.     "normalize",
  4434.     "normalizeDocument",
  4435.     "getFeature",
  4436.     "getUserData",
  4437.     "setUserData"
  4438. ]);
  4439.  
  4440. domMemberMap.Element = extendArray(domMemberMap.Node,
  4441. [
  4442.     "clientWidth",
  4443.     "clientHeight",
  4444.     "offsetLeft",
  4445.     "offsetTop",
  4446.     "offsetWidth",
  4447.     "offsetHeight",
  4448.     "scrollLeft",
  4449.     "scrollTop",
  4450.     "scrollWidth",
  4451.     "scrollHeight",
  4452.  
  4453.     "style",
  4454.  
  4455.     "tabIndex",
  4456.     "title",
  4457.     "lang",
  4458.     "align",
  4459.     "spellcheck",
  4460.  
  4461.     "addEventListener",
  4462.     "removeEventListener",
  4463.     "dispatchEvent",
  4464.     "focus",
  4465.     "blur",
  4466.     "cloneNode",
  4467.     "appendChild",
  4468.     "insertBefore",
  4469.     "replaceChild",
  4470.     "removeChild",
  4471.     "compareDocumentPosition",
  4472.     "getElementsByTagName",
  4473.     "getElementsByTagNameNS",
  4474.     "getAttribute",
  4475.     "getAttributeNS",
  4476.     "getAttributeNode",
  4477.     "getAttributeNodeNS",
  4478.     "setAttribute",
  4479.     "setAttributeNS",
  4480.     "setAttributeNode",
  4481.     "setAttributeNodeNS",
  4482.     "removeAttribute",
  4483.     "removeAttributeNS",
  4484.     "removeAttributeNode",
  4485.     "hasAttribute",
  4486.     "hasAttributeNS",
  4487.     "hasAttributes",
  4488.     "hasChildNodes",
  4489.     "lookupNamespaceURI",
  4490.     "lookupPrefix",
  4491.     "normalize",
  4492.     "isDefaultNamespace",
  4493.     "isEqualNode",
  4494.     "isSameNode",
  4495.     "isSupported",
  4496.     "getFeature",
  4497.     "getUserData",
  4498.     "setUserData"
  4499. ]);
  4500.  
  4501. domMemberMap.SVGElement = extendArray(domMemberMap.Element,
  4502. [
  4503.     "x",
  4504.     "y",
  4505.     "width",
  4506.     "height",
  4507.     "rx",
  4508.     "ry",
  4509.     "transform",
  4510.     "href",
  4511.  
  4512.     "ownerSVGElement",
  4513.     "viewportElement",
  4514.     "farthestViewportElement",
  4515.     "nearestViewportElement",
  4516.  
  4517.     "getBBox",
  4518.     "getCTM",
  4519.     "getScreenCTM",
  4520.     "getTransformToElement",
  4521.     "getPresentationAttribute",
  4522.     "preserveAspectRatio"
  4523. ]);
  4524.  
  4525. domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element,
  4526. [
  4527.     "x",
  4528.     "y",
  4529.     "width",
  4530.     "height",
  4531.     "rx",
  4532.     "ry",
  4533.     "transform",
  4534.  
  4535.     "viewBox",
  4536.     "viewport",
  4537.     "currentView",
  4538.     "useCurrentView",
  4539.     "pixelUnitToMillimeterX",
  4540.     "pixelUnitToMillimeterY",
  4541.     "screenPixelToMillimeterX",
  4542.     "screenPixelToMillimeterY",
  4543.     "currentScale",
  4544.     "currentTranslate",
  4545.     "zoomAndPan",
  4546.  
  4547.     "ownerSVGElement",
  4548.     "viewportElement",
  4549.     "farthestViewportElement",
  4550.     "nearestViewportElement",
  4551.     "contentScriptType",
  4552.     "contentStyleType",
  4553.  
  4554.     "getBBox",
  4555.     "getCTM",
  4556.     "getScreenCTM",
  4557.     "getTransformToElement",
  4558.     "getEnclosureList",
  4559.     "getIntersectionList",
  4560.     "getViewboxToViewportTransform",
  4561.     "getPresentationAttribute",
  4562.     "getElementById",
  4563.     "checkEnclosure",
  4564.     "checkIntersection",
  4565.     "createSVGAngle",
  4566.     "createSVGLength",
  4567.     "createSVGMatrix",
  4568.     "createSVGNumber",
  4569.     "createSVGPoint",
  4570.     "createSVGRect",
  4571.     "createSVGString",
  4572.     "createSVGTransform",
  4573.     "createSVGTransformFromMatrix",
  4574.     "deSelectAll",
  4575.     "preserveAspectRatio",
  4576.     "forceRedraw",
  4577.     "suspendRedraw",
  4578.     "unsuspendRedraw",
  4579.     "unsuspendRedrawAll",
  4580.     "getCurrentTime",
  4581.     "setCurrentTime",
  4582.     "animationsPaused",
  4583.     "pauseAnimations",
  4584.     "unpauseAnimations"
  4585. ]);
  4586.  
  4587. domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element,
  4588. [
  4589.     "src",
  4590.     "naturalWidth",
  4591.     "naturalHeight",
  4592.     "width",
  4593.     "height",
  4594.     "x",
  4595.     "y",
  4596.     "name",
  4597.     "alt",
  4598.     "longDesc",
  4599.     "lowsrc",
  4600.     "border",
  4601.     "complete",
  4602.     "hspace",
  4603.     "vspace",
  4604.     "isMap",
  4605.     "useMap",
  4606. ]);
  4607.  
  4608. domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element,
  4609. [
  4610.     "name",
  4611.     "target",
  4612.     "accessKey",
  4613.     "href",
  4614.     "protocol",
  4615.     "host",
  4616.     "hostname",
  4617.     "port",
  4618.     "pathname",
  4619.     "search",
  4620.     "hash",
  4621.     "hreflang",
  4622.     "coords",
  4623.     "shape",
  4624.     "text",
  4625.     "type",
  4626.     "rel",
  4627.     "rev",
  4628.     "charset"
  4629. ]);
  4630.  
  4631. domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element,
  4632. [
  4633.     "contentDocument",
  4634.     "contentWindow",
  4635.     "frameBorder",
  4636.     "height",
  4637.     "longDesc",
  4638.     "marginHeight",
  4639.     "marginWidth",
  4640.     "name",
  4641.     "scrolling",
  4642.     "src",
  4643.     "width"
  4644. ]);
  4645.  
  4646. domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element,
  4647. [
  4648.     "bgColor",
  4649.     "border",
  4650.     "caption",
  4651.     "cellPadding",
  4652.     "cellSpacing",
  4653.     "frame",
  4654.     "rows",
  4655.     "rules",
  4656.     "summary",
  4657.     "tBodies",
  4658.     "tFoot",
  4659.     "tHead",
  4660.     "width",
  4661.  
  4662.     "createCaption",
  4663.     "createTFoot",
  4664.     "createTHead",
  4665.     "deleteCaption",
  4666.     "deleteRow",
  4667.     "deleteTFoot",
  4668.     "deleteTHead",
  4669.     "insertRow"
  4670. ]);
  4671.  
  4672. domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element,
  4673. [
  4674.     "bgColor",
  4675.     "cells",
  4676.     "ch",
  4677.     "chOff",
  4678.     "rowIndex",
  4679.     "sectionRowIndex",
  4680.     "vAlign",
  4681.  
  4682.     "deleteCell",
  4683.     "insertCell"
  4684. ]);
  4685.  
  4686. domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element,
  4687. [
  4688.     "abbr",
  4689.     "axis",
  4690.     "bgColor",
  4691.     "cellIndex",
  4692.     "ch",
  4693.     "chOff",
  4694.     "colSpan",
  4695.     "headers",
  4696.     "height",
  4697.     "noWrap",
  4698.     "rowSpan",
  4699.     "scope",
  4700.     "vAlign",
  4701.     "width"
  4702.  
  4703. ]);
  4704.  
  4705. domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
  4706. [
  4707.     "src"
  4708. ]);
  4709.  
  4710. domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
  4711. [
  4712.     "accessKey",
  4713.     "disabled",
  4714.     "form",
  4715.     "name",
  4716.     "type",
  4717.     "value",
  4718.  
  4719.     "click"
  4720. ]);
  4721.  
  4722. domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element,
  4723. [
  4724.     "type",
  4725.     "value",
  4726.     "checked",
  4727.     "accept",
  4728.     "accessKey",
  4729.     "alt",
  4730.     "controllers",
  4731.     "defaultChecked",
  4732.     "defaultValue",
  4733.     "disabled",
  4734.     "form",
  4735.     "maxLength",
  4736.     "name",
  4737.     "readOnly",
  4738.     "selectionEnd",
  4739.     "selectionStart",
  4740.     "size",
  4741.     "src",
  4742.     "textLength",
  4743.     "useMap",
  4744.  
  4745.     "click",
  4746.     "select",
  4747.     "setSelectionRange"
  4748. ]);
  4749.  
  4750. domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element,
  4751. [
  4752.     "acceptCharset",
  4753.     "action",
  4754.     "author",
  4755.     "elements",
  4756.     "encoding",
  4757.     "enctype",
  4758.     "entry_id",
  4759.     "length",
  4760.     "method",
  4761.     "name",
  4762.     "post",
  4763.     "target",
  4764.     "text",
  4765.     "url",
  4766.  
  4767.     "reset",
  4768.     "submit"
  4769. ]);
  4770.  
  4771. domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
  4772. [
  4773.     "aLink",
  4774.     "background",
  4775.     "bgColor",
  4776.     "link",
  4777.     "text",
  4778.     "vLink"
  4779. ]);
  4780.  
  4781. domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
  4782. [
  4783.     "version"
  4784. ]);
  4785.  
  4786. domMemberMap.Text = extendArray(domMemberMap.Node,
  4787. [
  4788.     "data",
  4789.     "length",
  4790.  
  4791.     "appendData",
  4792.     "deleteData",
  4793.     "insertData",
  4794.     "replaceData",
  4795.     "splitText",
  4796.     "substringData"
  4797. ]);
  4798.  
  4799. domMemberMap.Attr = extendArray(domMemberMap.Node,
  4800. [
  4801.     "name",
  4802.     "value",
  4803.     "specified",
  4804.     "ownerElement"
  4805. ]);
  4806.  
  4807. domMemberMap.Event =
  4808. [
  4809.     "type",
  4810.     "target",
  4811.     "currentTarget",
  4812.     "originalTarget",
  4813.     "explicitOriginalTarget",
  4814.     "relatedTarget",
  4815.     "rangeParent",
  4816.     "rangeOffset",
  4817.     "view",
  4818.  
  4819.     "keyCode",
  4820.     "charCode",
  4821.     "screenX",
  4822.     "screenY",
  4823.     "clientX",
  4824.     "clientY",
  4825.     "layerX",
  4826.     "layerY",
  4827.     "pageX",
  4828.     "pageY",
  4829.  
  4830.     "detail",
  4831.     "button",
  4832.     "which",
  4833.     "ctrlKey",
  4834.     "shiftKey",
  4835.     "altKey",
  4836.     "metaKey",
  4837.  
  4838.     "eventPhase",
  4839.     "timeStamp",
  4840.     "bubbles",
  4841.     "cancelable",
  4842.     "cancelBubble",
  4843.  
  4844.     "isTrusted",
  4845.     "isChar",
  4846.  
  4847.     "getPreventDefault",
  4848.     "initEvent",
  4849.     "initMouseEvent",
  4850.     "initKeyEvent",
  4851.     "initUIEvent",
  4852.     "preventBubble",
  4853.     "preventCapture",
  4854.     "preventDefault",
  4855.     "stopPropagation"
  4856. ];
  4857.  
  4858. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4859.  
  4860. this.domConstantMap =
  4861. {
  4862.     "ELEMENT_NODE": 1,
  4863.     "ATTRIBUTE_NODE": 1,
  4864.     "TEXT_NODE": 1,
  4865.     "CDATA_SECTION_NODE": 1,
  4866.     "ENTITY_REFERENCE_NODE": 1,
  4867.     "ENTITY_NODE": 1,
  4868.     "PROCESSING_INSTRUCTION_NODE": 1,
  4869.     "COMMENT_NODE": 1,
  4870.     "DOCUMENT_NODE": 1,
  4871.     "DOCUMENT_TYPE_NODE": 1,
  4872.     "DOCUMENT_FRAGMENT_NODE": 1,
  4873.     "NOTATION_NODE": 1,
  4874.  
  4875.     "DOCUMENT_POSITION_DISCONNECTED": 1,
  4876.     "DOCUMENT_POSITION_PRECEDING": 1,
  4877.     "DOCUMENT_POSITION_FOLLOWING": 1,
  4878.     "DOCUMENT_POSITION_CONTAINS": 1,
  4879.     "DOCUMENT_POSITION_CONTAINED_BY": 1,
  4880.     "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1,
  4881.  
  4882.     "UNKNOWN_RULE": 1,
  4883.     "STYLE_RULE": 1,
  4884.     "CHARSET_RULE": 1,
  4885.     "IMPORT_RULE": 1,
  4886.     "MEDIA_RULE": 1,
  4887.     "FONT_FACE_RULE": 1,
  4888.     "PAGE_RULE": 1,
  4889.  
  4890.     "CAPTURING_PHASE": 1,
  4891.     "AT_TARGET": 1,
  4892.     "BUBBLING_PHASE": 1,
  4893.  
  4894.     "SCROLL_PAGE_UP": 1,
  4895.     "SCROLL_PAGE_DOWN": 1,
  4896.  
  4897.     "MOUSEUP": 1,
  4898.     "MOUSEDOWN": 1,
  4899.     "MOUSEOVER": 1,
  4900.     "MOUSEOUT": 1,
  4901.     "MOUSEMOVE": 1,
  4902.     "MOUSEDRAG": 1,
  4903.     "CLICK": 1,
  4904.     "DBLCLICK": 1,
  4905.     "KEYDOWN": 1,
  4906.     "KEYUP": 1,
  4907.     "KEYPRESS": 1,
  4908.     "DRAGDROP": 1,
  4909.     "FOCUS": 1,
  4910.     "BLUR": 1,
  4911.     "SELECT": 1,
  4912.     "CHANGE": 1,
  4913.     "RESET": 1,
  4914.     "SUBMIT": 1,
  4915.     "SCROLL": 1,
  4916.     "LOAD": 1,
  4917.     "UNLOAD": 1,
  4918.     "XFER_DONE": 1,
  4919.     "ABORT": 1,
  4920.     "ERROR": 1,
  4921.     "LOCATE": 1,
  4922.     "MOVE": 1,
  4923.     "RESIZE": 1,
  4924.     "FORWARD": 1,
  4925.     "HELP": 1,
  4926.     "BACK": 1,
  4927.     "TEXT": 1,
  4928.  
  4929.     "ALT_MASK": 1,
  4930.     "CONTROL_MASK": 1,
  4931.     "SHIFT_MASK": 1,
  4932.     "META_MASK": 1,
  4933.  
  4934.     "DOM_VK_TAB": 1,
  4935.     "DOM_VK_PAGE_UP": 1,
  4936.     "DOM_VK_PAGE_DOWN": 1,
  4937.     "DOM_VK_UP": 1,
  4938.     "DOM_VK_DOWN": 1,
  4939.     "DOM_VK_LEFT": 1,
  4940.     "DOM_VK_RIGHT": 1,
  4941.     "DOM_VK_CANCEL": 1,
  4942.     "DOM_VK_HELP": 1,
  4943.     "DOM_VK_BACK_SPACE": 1,
  4944.     "DOM_VK_CLEAR": 1,
  4945.     "DOM_VK_RETURN": 1,
  4946.     "DOM_VK_ENTER": 1,
  4947.     "DOM_VK_SHIFT": 1,
  4948.     "DOM_VK_CONTROL": 1,
  4949.     "DOM_VK_ALT": 1,
  4950.     "DOM_VK_PAUSE": 1,
  4951.     "DOM_VK_CAPS_LOCK": 1,
  4952.     "DOM_VK_ESCAPE": 1,
  4953.     "DOM_VK_SPACE": 1,
  4954.     "DOM_VK_END": 1,
  4955.     "DOM_VK_HOME": 1,
  4956.     "DOM_VK_PRINTSCREEN": 1,
  4957.     "DOM_VK_INSERT": 1,
  4958.     "DOM_VK_DELETE": 1,
  4959.     "DOM_VK_0": 1,
  4960.     "DOM_VK_1": 1,
  4961.     "DOM_VK_2": 1,
  4962.     "DOM_VK_3": 1,
  4963.     "DOM_VK_4": 1,
  4964.     "DOM_VK_5": 1,
  4965.     "DOM_VK_6": 1,
  4966.     "DOM_VK_7": 1,
  4967.     "DOM_VK_8": 1,
  4968.     "DOM_VK_9": 1,
  4969.     "DOM_VK_SEMICOLON": 1,
  4970.     "DOM_VK_EQUALS": 1,
  4971.     "DOM_VK_A": 1,
  4972.     "DOM_VK_B": 1,
  4973.     "DOM_VK_C": 1,
  4974.     "DOM_VK_D": 1,
  4975.     "DOM_VK_E": 1,
  4976.     "DOM_VK_F": 1,
  4977.     "DOM_VK_G": 1,
  4978.     "DOM_VK_H": 1,
  4979.     "DOM_VK_I": 1,
  4980.     "DOM_VK_J": 1,
  4981.     "DOM_VK_K": 1,
  4982.     "DOM_VK_L": 1,
  4983.     "DOM_VK_M": 1,
  4984.     "DOM_VK_N": 1,
  4985.     "DOM_VK_O": 1,
  4986.     "DOM_VK_P": 1,
  4987.     "DOM_VK_Q": 1,
  4988.     "DOM_VK_R": 1,
  4989.     "DOM_VK_S": 1,
  4990.     "DOM_VK_T": 1,
  4991.     "DOM_VK_U": 1,
  4992.     "DOM_VK_V": 1,
  4993.     "DOM_VK_W": 1,
  4994.     "DOM_VK_X": 1,
  4995.     "DOM_VK_Y": 1,
  4996.     "DOM_VK_Z": 1,
  4997.     "DOM_VK_CONTEXT_MENU": 1,
  4998.     "DOM_VK_NUMPAD0": 1,
  4999.     "DOM_VK_NUMPAD1": 1,
  5000.     "DOM_VK_NUMPAD2": 1,
  5001.     "DOM_VK_NUMPAD3": 1,
  5002.     "DOM_VK_NUMPAD4": 1,
  5003.     "DOM_VK_NUMPAD5": 1,
  5004.     "DOM_VK_NUMPAD6": 1,
  5005.     "DOM_VK_NUMPAD7": 1,
  5006.     "DOM_VK_NUMPAD8": 1,
  5007.     "DOM_VK_NUMPAD9": 1,
  5008.     "DOM_VK_MULTIPLY": 1,
  5009.     "DOM_VK_ADD": 1,
  5010.     "DOM_VK_SEPARATOR": 1,
  5011.     "DOM_VK_SUBTRACT": 1,
  5012.     "DOM_VK_DECIMAL": 1,
  5013.     "DOM_VK_DIVIDE": 1,
  5014.     "DOM_VK_F1": 1,
  5015.     "DOM_VK_F2": 1,
  5016.     "DOM_VK_F3": 1,
  5017.     "DOM_VK_F4": 1,
  5018.     "DOM_VK_F5": 1,
  5019.     "DOM_VK_F6": 1,
  5020.     "DOM_VK_F7": 1,
  5021.     "DOM_VK_F8": 1,
  5022.     "DOM_VK_F9": 1,
  5023.     "DOM_VK_F10": 1,
  5024.     "DOM_VK_F11": 1,
  5025.     "DOM_VK_F12": 1,
  5026.     "DOM_VK_F13": 1,
  5027.     "DOM_VK_F14": 1,
  5028.     "DOM_VK_F15": 1,
  5029.     "DOM_VK_F16": 1,
  5030.     "DOM_VK_F17": 1,
  5031.     "DOM_VK_F18": 1,
  5032.     "DOM_VK_F19": 1,
  5033.     "DOM_VK_F20": 1,
  5034.     "DOM_VK_F21": 1,
  5035.     "DOM_VK_F22": 1,
  5036.     "DOM_VK_F23": 1,
  5037.     "DOM_VK_F24": 1,
  5038.     "DOM_VK_NUM_LOCK": 1,
  5039.     "DOM_VK_SCROLL_LOCK": 1,
  5040.     "DOM_VK_COMMA": 1,
  5041.     "DOM_VK_PERIOD": 1,
  5042.     "DOM_VK_SLASH": 1,
  5043.     "DOM_VK_BACK_QUOTE": 1,
  5044.     "DOM_VK_OPEN_BRACKET": 1,
  5045.     "DOM_VK_BACK_SLASH": 1,
  5046.     "DOM_VK_CLOSE_BRACKET": 1,
  5047.     "DOM_VK_QUOTE": 1,
  5048.     "DOM_VK_META": 1,
  5049.  
  5050.     "SVG_ZOOMANDPAN_DISABLE": 1,
  5051.     "SVG_ZOOMANDPAN_MAGNIFY": 1,
  5052.     "SVG_ZOOMANDPAN_UNKNOWN": 1
  5053. };
  5054.  
  5055. this.cssInfo =
  5056. {
  5057.     "background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"],
  5058.     "background-attachment": ["bgAttachment"],
  5059.     "background-color": ["color", "systemColor"],
  5060.     "background-image": ["none"],
  5061.     "background-position": ["bgPosition"],
  5062.     "background-repeat": ["bgRepeat"],
  5063.  
  5064.     "border": ["borderStyle", "thickness", "color", "systemColor", "none"],
  5065.     "border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5066.     "border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5067.     "border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5068.     "border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5069.     "border-collapse": ["borderCollapse"],
  5070.     "border-color": ["color", "systemColor"],
  5071.     "border-top-color": ["color", "systemColor"],
  5072.     "border-right-color": ["color", "systemColor"],
  5073.     "border-bottom-color": ["color", "systemColor"],
  5074.     "border-left-color": ["color", "systemColor"],
  5075.     "border-spacing": [],
  5076.     "border-style": ["borderStyle"],
  5077.     "border-top-style": ["borderStyle"],
  5078.     "border-right-style": ["borderStyle"],
  5079.     "border-bottom-style": ["borderStyle"],
  5080.     "border-left-style": ["borderStyle"],
  5081.     "border-width": ["thickness"],
  5082.     "border-top-width": ["thickness"],
  5083.     "border-right-width": ["thickness"],
  5084.     "border-bottom-width": ["thickness"],
  5085.     "border-left-width": ["thickness"],
  5086.  
  5087.     "bottom": ["auto"],
  5088.     "caption-side": ["captionSide"],
  5089.     "clear": ["clear", "none"],
  5090.     "clip": ["auto"],
  5091.     "color": ["color", "systemColor"],
  5092.     "content": ["content"],
  5093.     "counter-increment": ["none"],
  5094.     "counter-reset": ["none"],
  5095.     "cursor": ["cursor", "none"],
  5096.     "direction": ["direction"],
  5097.     "display": ["display", "none"],
  5098.     "empty-cells": [],
  5099.     "float": ["float", "none"],
  5100.     "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"],
  5101.  
  5102.     "font-family": ["fontFamily"],
  5103.     "font-size": ["fontSize"],
  5104.     "font-size-adjust": [],
  5105.     "font-stretch": [],
  5106.     "font-style": ["fontStyle"],
  5107.     "font-variant": ["fontVariant"],
  5108.     "font-weight": ["fontWeight"],
  5109.  
  5110.     "height": ["auto"],
  5111.     "left": ["auto"],
  5112.     "letter-spacing": [],
  5113.     "line-height": [],
  5114.  
  5115.     "list-style": ["listStyleType", "listStylePosition", "none"],
  5116.     "list-style-image": ["none"],
  5117.     "list-style-position": ["listStylePosition"],
  5118.     "list-style-type": ["listStyleType", "none"],
  5119.  
  5120.     "margin": [],
  5121.     "margin-top": [],
  5122.     "margin-right": [],
  5123.     "margin-bottom": [],
  5124.     "margin-left": [],
  5125.  
  5126.     "marker-offset": ["auto"],
  5127.     "min-height": ["none"],
  5128.     "max-height": ["none"],
  5129.     "min-width": ["none"],
  5130.     "max-width": ["none"],
  5131.  
  5132.     "outline": ["borderStyle", "color", "systemColor", "none"],
  5133.     "outline-color": ["color", "systemColor"],
  5134.     "outline-style": ["borderStyle"],
  5135.     "outline-width": [],
  5136.  
  5137.     "overflow": ["overflow", "auto"],
  5138.     "overflow-x": ["overflow", "auto"],
  5139.     "overflow-y": ["overflow", "auto"],
  5140.  
  5141.     "padding": [],
  5142.     "padding-top": [],
  5143.     "padding-right": [],
  5144.     "padding-bottom": [],
  5145.     "padding-left": [],
  5146.  
  5147.     "position": ["position"],
  5148.     "quotes": ["none"],
  5149.     "right": ["auto"],
  5150.     "table-layout": ["tableLayout", "auto"],
  5151.     "text-align": ["textAlign"],
  5152.     "text-decoration": ["textDecoration", "none"],
  5153.     "text-indent": [],
  5154.     "text-shadow": [],
  5155.     "text-transform": ["textTransform", "none"],
  5156.     "top": ["auto"],
  5157.     "unicode-bidi": [],
  5158.     "vertical-align": ["verticalAlign"],
  5159.     "white-space": ["whiteSpace"],
  5160.     "width": ["auto"],
  5161.     "word-spacing": [],
  5162.     "z-index": [],
  5163.  
  5164.     "-moz-appearance": ["mozAppearance"],
  5165.     "-moz-border-radius": [],
  5166.     "-moz-border-radius-bottomleft": [],
  5167.     "-moz-border-radius-bottomright": [],
  5168.     "-moz-border-radius-topleft": [],
  5169.     "-moz-border-radius-topright": [],
  5170.     "-moz-border-top-colors": ["color", "systemColor"],
  5171.     "-moz-border-right-colors": ["color", "systemColor"],
  5172.     "-moz-border-bottom-colors": ["color", "systemColor"],
  5173.     "-moz-border-left-colors": ["color", "systemColor"],
  5174.     "-moz-box-align": ["mozBoxAlign"],
  5175.     "-moz-box-direction": ["mozBoxDirection"],
  5176.     "-moz-box-flex": [],
  5177.     "-moz-box-ordinal-group": [],
  5178.     "-moz-box-orient": ["mozBoxOrient"],
  5179.     "-moz-box-pack": ["mozBoxPack"],
  5180.     "-moz-box-sizing": ["mozBoxSizing"],
  5181.     "-moz-opacity": [],
  5182.     "-moz-user-focus": ["userFocus", "none"],
  5183.     "-moz-user-input": ["userInput"],
  5184.     "-moz-user-modify": [],
  5185.     "-moz-user-select": ["userSelect", "none"],
  5186.     "-moz-background-clip": [],
  5187.     "-moz-background-inline-policy": [],
  5188.     "-moz-background-origin": [],
  5189.     "-moz-binding": [],
  5190.     "-moz-column-count": [],
  5191.     "-moz-column-gap": [],
  5192.     "-moz-column-width": [],
  5193.     "-moz-image-region": []
  5194. };
  5195.  
  5196. this.inheritedStyleNames =
  5197. {
  5198.     "border-collapse": 1,
  5199.     "border-spacing": 1,
  5200.     "border-style": 1,
  5201.     "caption-side": 1,
  5202.     "color": 1,
  5203.     "cursor": 1,
  5204.     "direction": 1,
  5205.     "empty-cells": 1,
  5206.     "font": 1,
  5207.     "font-family": 1,
  5208.     "font-size-adjust": 1,
  5209.     "font-size": 1,
  5210.     "font-style": 1,
  5211.     "font-variant": 1,
  5212.     "font-weight": 1,
  5213.     "letter-spacing": 1,
  5214.     "line-height": 1,
  5215.     "list-style": 1,
  5216.     "list-style-image": 1,
  5217.     "list-style-position": 1,
  5218.     "list-style-type": 1,
  5219.     "quotes": 1,
  5220.     "text-align": 1,
  5221.     "text-decoration": 1,
  5222.     "text-indent": 1,
  5223.     "text-shadow": 1,
  5224.     "text-transform": 1,
  5225.     "white-space": 1,
  5226.     "word-spacing": 1
  5227. };
  5228.  
  5229. this.cssKeywords =
  5230. {
  5231.     "appearance":
  5232.     [
  5233.         "button",
  5234.         "button-small",
  5235.         "checkbox",
  5236.         "checkbox-container",
  5237.         "checkbox-small",
  5238.         "dialog",
  5239.         "listbox",
  5240.         "menuitem",
  5241.         "menulist",
  5242.         "menulist-button",
  5243.         "menulist-textfield",
  5244.         "menupopup",
  5245.         "progressbar",
  5246.         "radio",
  5247.         "radio-container",
  5248.         "radio-small",
  5249.         "resizer",
  5250.         "scrollbar",
  5251.         "scrollbarbutton-down",
  5252.         "scrollbarbutton-left",
  5253.         "scrollbarbutton-right",
  5254.         "scrollbarbutton-up",
  5255.         "scrollbartrack-horizontal",
  5256.         "scrollbartrack-vertical",
  5257.         "separator",
  5258.         "statusbar",
  5259.         "tab",
  5260.         "tab-left-edge",
  5261.         "tabpanels",
  5262.         "textfield",
  5263.         "toolbar",
  5264.         "toolbarbutton",
  5265.         "toolbox",
  5266.         "tooltip",
  5267.         "treeheadercell",
  5268.         "treeheadersortarrow",
  5269.         "treeitem",
  5270.         "treetwisty",
  5271.         "treetwistyopen",
  5272.         "treeview",
  5273.         "window"
  5274.     ],
  5275.  
  5276.     "systemColor":
  5277.     [
  5278.         "ActiveBorder",
  5279.         "ActiveCaption",
  5280.         "AppWorkspace",
  5281.         "Background",
  5282.         "ButtonFace",
  5283.         "ButtonHighlight",
  5284.         "ButtonShadow",
  5285.         "ButtonText",
  5286.         "CaptionText",
  5287.         "GrayText",
  5288.         "Highlight",
  5289.         "HighlightText",
  5290.         "InactiveBorder",
  5291.         "InactiveCaption",
  5292.         "InactiveCaptionText",
  5293.         "InfoBackground",
  5294.         "InfoText",
  5295.         "Menu",
  5296.         "MenuText",
  5297.         "Scrollbar",
  5298.         "ThreeDDarkShadow",
  5299.         "ThreeDFace",
  5300.         "ThreeDHighlight",
  5301.         "ThreeDLightShadow",
  5302.         "ThreeDShadow",
  5303.         "Window",
  5304.         "WindowFrame",
  5305.         "WindowText",
  5306.         "-moz-field",
  5307.         "-moz-fieldtext",
  5308.         "-moz-workspace",
  5309.         "-moz-visitedhyperlinktext",
  5310.         "-moz-use-text-color"
  5311.     ],
  5312.  
  5313.     "color":
  5314.     [
  5315.         "AliceBlue",
  5316.         "AntiqueWhite",
  5317.         "Aqua",
  5318.         "Aquamarine",
  5319.         "Azure",
  5320.         "Beige",
  5321.         "Bisque",
  5322.         "Black",
  5323.         "BlanchedAlmond",
  5324.         "Blue",
  5325.         "BlueViolet",
  5326.         "Brown",
  5327.         "BurlyWood",
  5328.         "CadetBlue",
  5329.         "Chartreuse",
  5330.         "Chocolate",
  5331.         "Coral",
  5332.         "CornflowerBlue",
  5333.         "Cornsilk",
  5334.         "Crimson",
  5335.         "Cyan",
  5336.         "DarkBlue",
  5337.         "DarkCyan",
  5338.         "DarkGoldenRod",
  5339.         "DarkGray",
  5340.         "DarkGreen",
  5341.         "DarkKhaki",
  5342.         "DarkMagenta",
  5343.         "DarkOliveGreen",
  5344.         "DarkOrange",
  5345.         "DarkOrchid",
  5346.         "DarkRed",
  5347.         "DarkSalmon",
  5348.         "DarkSeaGreen",
  5349.         "DarkSlateBlue",
  5350.         "DarkSlateGray",
  5351.         "DarkTurquoise",
  5352.         "DarkViolet",
  5353.         "DeepPink",
  5354.         "DarkSkyBlue",
  5355.         "DimGray",
  5356.         "DodgerBlue",
  5357.         "Feldspar",
  5358.         "FireBrick",
  5359.         "FloralWhite",
  5360.         "ForestGreen",
  5361.         "Fuchsia",
  5362.         "Gainsboro",
  5363.         "GhostWhite",
  5364.         "Gold",
  5365.         "GoldenRod",
  5366.         "Gray",
  5367.         "Green",
  5368.         "GreenYellow",
  5369.         "HoneyDew",
  5370.         "HotPink",
  5371.         "IndianRed",
  5372.         "Indigo",
  5373.         "Ivory",
  5374.         "Khaki",
  5375.         "Lavender",
  5376.         "LavenderBlush",
  5377.         "LawnGreen",
  5378.         "LemonChiffon",
  5379.         "LightBlue",
  5380.         "LightCoral",
  5381.         "LightCyan",
  5382.         "LightGoldenRodYellow",
  5383.         "LightGrey",
  5384.         "LightGreen",
  5385.         "LightPink",
  5386.         "LightSalmon",
  5387.         "LightSeaGreen",
  5388.         "LightSkyBlue",
  5389.         "LightSlateBlue",
  5390.         "LightSlateGray",
  5391.         "LightSteelBlue",
  5392.         "LightYellow",
  5393.         "Lime",
  5394.         "LimeGreen",
  5395.         "Linen",
  5396.         "Magenta",
  5397.         "Maroon",
  5398.         "MediumAquaMarine",
  5399.         "MediumBlue",
  5400.         "MediumOrchid",
  5401.         "MediumPurple",
  5402.         "MediumSeaGreen",
  5403.         "MediumSlateBlue",
  5404.         "MediumSpringGreen",
  5405.         "MediumTurquoise",
  5406.         "MediumVioletRed",
  5407.         "MidnightBlue",
  5408.         "MintCream",
  5409.         "MistyRose",
  5410.         "Moccasin",
  5411.         "NavajoWhite",
  5412.         "Navy",
  5413.         "OldLace",
  5414.         "Olive",
  5415.         "OliveDrab",
  5416.         "Orange",
  5417.         "OrangeRed",
  5418.         "Orchid",
  5419.         "PaleGoldenRod",
  5420.         "PaleGreen",
  5421.         "PaleTurquoise",
  5422.         "PaleVioletRed",
  5423.         "PapayaWhip",
  5424.         "PeachPuff",
  5425.         "Peru",
  5426.         "Pink",
  5427.         "Plum",
  5428.         "PowderBlue",
  5429.         "Purple",
  5430.         "Red",
  5431.         "RosyBrown",
  5432.         "RoyalBlue",
  5433.         "SaddleBrown",
  5434.         "Salmon",
  5435.         "SandyBrown",
  5436.         "SeaGreen",
  5437.         "SeaShell",
  5438.         "Sienna",
  5439.         "Silver",
  5440.         "SkyBlue",
  5441.         "SlateBlue",
  5442.         "SlateGray",
  5443.         "Snow",
  5444.         "SpringGreen",
  5445.         "SteelBlue",
  5446.         "Tan",
  5447.         "Teal",
  5448.         "Thistle",
  5449.         "Tomato",
  5450.         "Turquoise",
  5451.         "Violet",
  5452.         "VioletRed",
  5453.         "Wheat",
  5454.         "White",
  5455.         "WhiteSmoke",
  5456.         "Yellow",
  5457.         "YellowGreen",
  5458.         "transparent",
  5459.         "invert"
  5460.     ],
  5461.  
  5462.     "auto":
  5463.     [
  5464.         "auto"
  5465.     ],
  5466.  
  5467.     "none":
  5468.     [
  5469.         "none"
  5470.     ],
  5471.  
  5472.     "captionSide":
  5473.     [
  5474.         "top",
  5475.         "bottom",
  5476.         "left",
  5477.         "right"
  5478.     ],
  5479.  
  5480.     "clear":
  5481.     [
  5482.         "left",
  5483.         "right",
  5484.         "both"
  5485.     ],
  5486.  
  5487.     "cursor":
  5488.     [
  5489.         "auto",
  5490.         "cell",
  5491.         "context-menu",
  5492.         "crosshair",
  5493.         "default",
  5494.         "help",
  5495.         "pointer",
  5496.         "progress",
  5497.         "move",
  5498.         "e-resize",
  5499.         "all-scroll",
  5500.         "ne-resize",
  5501.         "nw-resize",
  5502.         "n-resize",
  5503.         "se-resize",
  5504.         "sw-resize",
  5505.         "s-resize",
  5506.         "w-resize",
  5507.         "ew-resize",
  5508.         "ns-resize",
  5509.         "nesw-resize",
  5510.         "nwse-resize",
  5511.         "col-resize",
  5512.         "row-resize",
  5513.         "text",
  5514.         "vertical-text",
  5515.         "wait",
  5516.         "alias",
  5517.         "copy",
  5518.         "move",
  5519.         "no-drop",
  5520.         "not-allowed",
  5521.         "-moz-alias",
  5522.         "-moz-cell",
  5523.         "-moz-copy",
  5524.         "-moz-grab",
  5525.         "-moz-grabbing",
  5526.         "-moz-contextmenu",
  5527.         "-moz-zoom-in",
  5528.         "-moz-zoom-out",
  5529.         "-moz-spinning"
  5530.     ],
  5531.  
  5532.     "direction":
  5533.     [
  5534.         "ltr",
  5535.         "rtl"
  5536.     ],
  5537.  
  5538.     "bgAttachment":
  5539.     [
  5540.         "scroll",
  5541.         "fixed"
  5542.     ],
  5543.  
  5544.     "bgPosition":
  5545.     [
  5546.         "top",
  5547.         "center",
  5548.         "bottom",
  5549.         "left",
  5550.         "right"
  5551.     ],
  5552.  
  5553.     "bgRepeat":
  5554.     [
  5555.         "repeat",
  5556.         "repeat-x",
  5557.         "repeat-y",
  5558.         "no-repeat"
  5559.     ],
  5560.  
  5561.     "borderStyle":
  5562.     [
  5563.         "hidden",
  5564.         "dotted",
  5565.         "dashed",
  5566.         "solid",
  5567.         "double",
  5568.         "groove",
  5569.         "ridge",
  5570.         "inset",
  5571.         "outset",
  5572.         "-moz-bg-inset",
  5573.         "-moz-bg-outset",
  5574.         "-moz-bg-solid"
  5575.     ],
  5576.  
  5577.     "borderCollapse":
  5578.     [
  5579.         "collapse",
  5580.         "separate"
  5581.     ],
  5582.  
  5583.     "overflow":
  5584.     [
  5585.         "visible",
  5586.         "hidden",
  5587.         "scroll",
  5588.         "-moz-scrollbars-horizontal",
  5589.         "-moz-scrollbars-none",
  5590.         "-moz-scrollbars-vertical"
  5591.     ],
  5592.  
  5593.     "listStyleType":
  5594.     [
  5595.         "disc",
  5596.         "circle",
  5597.         "square",
  5598.         "decimal",
  5599.         "decimal-leading-zero",
  5600.         "lower-roman",
  5601.         "upper-roman",
  5602.         "lower-greek",
  5603.         "lower-alpha",
  5604.         "lower-latin",
  5605.         "upper-alpha",
  5606.         "upper-latin",
  5607.         "hebrew",
  5608.         "armenian",
  5609.         "georgian",
  5610.         "cjk-ideographic",
  5611.         "hiragana",
  5612.         "katakana",
  5613.         "hiragana-iroha",
  5614.         "katakana-iroha",
  5615.         "inherit"
  5616.     ],
  5617.  
  5618.     "listStylePosition":
  5619.     [
  5620.         "inside",
  5621.         "outside"
  5622.     ],
  5623.  
  5624.     "content":
  5625.     [
  5626.         "open-quote",
  5627.         "close-quote",
  5628.         "no-open-quote",
  5629.         "no-close-quote",
  5630.         "inherit"
  5631.     ],
  5632.  
  5633.     "fontStyle":
  5634.     [
  5635.         "normal",
  5636.         "italic",
  5637.         "oblique",
  5638.         "inherit"
  5639.     ],
  5640.  
  5641.     "fontVariant":
  5642.     [
  5643.         "normal",
  5644.         "small-caps",
  5645.         "inherit"
  5646.     ],
  5647.  
  5648.     "fontWeight":
  5649.     [
  5650.         "normal",
  5651.         "bold",
  5652.         "bolder",
  5653.         "lighter",
  5654.         "inherit"
  5655.     ],
  5656.  
  5657.     "fontSize":
  5658.     [
  5659.         "xx-small",
  5660.         "x-small",
  5661.         "small",
  5662.         "medium",
  5663.         "large",
  5664.         "x-large",
  5665.         "xx-large",
  5666.         "smaller",
  5667.         "larger"
  5668.     ],
  5669.  
  5670.     "fontFamily":
  5671.     [
  5672.         "Arial",
  5673.         "Comic Sans MS",
  5674.         "Georgia",
  5675.         "Tahoma",
  5676.         "Verdana",
  5677.         "Times New Roman",
  5678.         "Trebuchet MS",
  5679.         "Lucida Grande",
  5680.         "Helvetica",
  5681.         "serif",
  5682.         "sans-serif",
  5683.         "cursive",
  5684.         "fantasy",
  5685.         "monospace",
  5686.         "caption",
  5687.         "icon",
  5688.         "menu",
  5689.         "message-box",
  5690.         "small-caption",
  5691.         "status-bar",
  5692.         "inherit"
  5693.     ],
  5694.  
  5695.     "display":
  5696.     [
  5697.         "block",
  5698.         "inline",
  5699.         "list-item",
  5700.         "marker",
  5701.         "run-in",
  5702.         "compact",
  5703.         "table",
  5704.         "inline-table",
  5705.         "table-row-group",
  5706.         "table-column",
  5707.         "table-column-group",
  5708.         "table-header-group",
  5709.         "table-footer-group",
  5710.         "table-row",
  5711.         "table-cell",
  5712.         "table-caption",
  5713.         "-moz-box",
  5714.         "-moz-compact",
  5715.         "-moz-deck",
  5716.         "-moz-grid",
  5717.         "-moz-grid-group",
  5718.         "-moz-grid-line",
  5719.         "-moz-groupbox",
  5720.         "-moz-inline-block",
  5721.         "-moz-inline-box",
  5722.         "-moz-inline-grid",
  5723.         "-moz-inline-stack",
  5724.         "-moz-inline-table",
  5725.         "-moz-marker",
  5726.         "-moz-popup",
  5727.         "-moz-runin",
  5728.         "-moz-stack"
  5729.     ],
  5730.  
  5731.     "position":
  5732.     [
  5733.         "static",
  5734.         "relative",
  5735.         "absolute",
  5736.         "fixed",
  5737.         "inherit"
  5738.     ],
  5739.  
  5740.     "float":
  5741.     [
  5742.         "left",
  5743.         "right"
  5744.     ],
  5745.  
  5746.     "textAlign":
  5747.     [
  5748.         "left",
  5749.         "right",
  5750.         "center",
  5751.         "justify"
  5752.     ],
  5753.  
  5754.     "tableLayout":
  5755.     [
  5756.         "fixed"
  5757.     ],
  5758.  
  5759.     "textDecoration":
  5760.     [
  5761.         "underline",
  5762.         "overline",
  5763.         "line-through",
  5764.         "blink"
  5765.     ],
  5766.  
  5767.     "textTransform":
  5768.     [
  5769.         "capitalize",
  5770.         "lowercase",
  5771.         "uppercase",
  5772.         "inherit"
  5773.     ],
  5774.  
  5775.     "unicodeBidi":
  5776.     [
  5777.         "normal",
  5778.         "embed",
  5779.         "bidi-override"
  5780.     ],
  5781.  
  5782.     "whiteSpace":
  5783.     [
  5784.         "normal",
  5785.         "pre",
  5786.         "nowrap"
  5787.     ],
  5788.  
  5789.     "verticalAlign":
  5790.     [
  5791.         "baseline",
  5792.         "sub",
  5793.         "super",
  5794.         "top",
  5795.         "text-top",
  5796.         "middle",
  5797.         "bottom",
  5798.         "text-bottom",
  5799.         "inherit"
  5800.     ],
  5801.  
  5802.     "thickness":
  5803.     [
  5804.         "thin",
  5805.         "medium",
  5806.         "thick"
  5807.     ],
  5808.  
  5809.     "userFocus":
  5810.     [
  5811.         "ignore",
  5812.         "normal"
  5813.     ],
  5814.  
  5815.     "userInput":
  5816.     [
  5817.         "disabled",
  5818.         "enabled"
  5819.     ],
  5820.  
  5821.     "userSelect":
  5822.     [
  5823.         "normal"
  5824.     ],
  5825.  
  5826.     "mozBoxSizing":
  5827.     [
  5828.         "content-box",
  5829.         "padding-box",
  5830.         "border-box"
  5831.     ],
  5832.  
  5833.     "mozBoxAlign":
  5834.     [
  5835.         "start",
  5836.         "center",
  5837.         "end",
  5838.         "baseline",
  5839.         "stretch"
  5840.     ],
  5841.  
  5842.     "mozBoxDirection":
  5843.     [
  5844.         "normal",
  5845.         "reverse"
  5846.     ],
  5847.  
  5848.     "mozBoxOrient":
  5849.     [
  5850.         "horizontal",
  5851.         "vertical"
  5852.     ],
  5853.  
  5854.     "mozBoxPack":
  5855.     [
  5856.         "start",
  5857.         "center",
  5858.         "end"
  5859.     ]
  5860. };
  5861.  
  5862. this.nonEditableTags =
  5863. {
  5864.     "HTML": 1,
  5865.     "HEAD": 1,
  5866.     "html": 1,
  5867.     "head": 1
  5868. };
  5869.  
  5870. this.innerEditableTags =
  5871. {
  5872.     "BODY": 1,
  5873.     "body": 1
  5874. };
  5875.  
  5876. const invisibleTags = this.invisibleTags =
  5877. {
  5878.     "HTML": 1,
  5879.     "HEAD": 1,
  5880.     "TITLE": 1,
  5881.     "META": 1,
  5882.     "LINK": 1,
  5883.     "STYLE": 1,
  5884.     "SCRIPT": 1,
  5885.     "NOSCRIPT": 1,
  5886.     "BR": 1,
  5887.  
  5888.     "html": 1,
  5889.     "head": 1,
  5890.     "title": 1,
  5891.     "meta": 1,
  5892.     "link": 1,
  5893.     "style": 1,
  5894.     "script": 1,
  5895.     "noscript": 1,
  5896.     "br": 1,
  5897.     /*
  5898.     "window": 1,
  5899.     "browser": 1,
  5900.     "frame": 1,
  5901.     "tabbrowser": 1,
  5902.     "WINDOW": 1,
  5903.     "BROWSER": 1,
  5904.     "FRAME": 1,
  5905.     "TABBROWSER": 1,
  5906.     */
  5907. };
  5908.  
  5909. // ************************************************************************************************
  5910. // Debug Logging
  5911.  
  5912. this.ERROR = function(exc)
  5913. {
  5914. }
  5915.  
  5916. // ************************************************************************************************
  5917. // Time Utils
  5918.  
  5919. this.formatTime = function(elapsed)
  5920. {
  5921.     if (elapsed == -1)
  5922.         return "_"; // should be   but this will be escaped so we need something that is no whitespace
  5923.     else if (elapsed < 1000)
  5924.         return elapsed + "ms";
  5925.     else if (elapsed < 60000)
  5926.         return (Math.ceil(elapsed/10) / 100) + "s";
  5927.     else
  5928.         return (Math.ceil((elapsed/60000)*100)/100) + "m";
  5929. }
  5930.  
  5931. // ************************************************************************************************
  5932.  
  5933. }).apply(FBL);
  5934. } catch(e) {                                                                            /*@explore*/
  5935.     dump("FBL Fails "+e+"\n");                                                                /*@explore*/
  5936.     dump("If the service @joehewitt.com/firebug;1 fails, try deleting compreg.dat, xpti.dat\n");/*@explore*/
  5937.     dump("Another cause can be mangled install.rdf.\n");/*@explore*/
  5938. }                                                                           /*@explore*/
  5939.